AppLoadingBar
The thin loading line of an application shell, shown while any part of the page reports that it is waiting.
App draws one thin, indeterminate line on the top edge of the content column while
something on the page is loading. Pages do not draw it: they report a wait, and the shell draws the
line in one place. For a local area that is loading, use
LoadingScreen.
Waiting: 0
// apps/docs/examples/ui/app-loading-bar.tsx'use client';
import {useState} from 'react';
import { AppLoadingBar, AppLoadingProvider, useAppLoadingRegistration,} from '@workspace/active-ui/components/composed/app-loading-bar';import {Button} from '@workspace/active-ui/components/primitives/button';
/** A source of waiting: registered while `active`. */function Wait({active}: {active: boolean}) { useAppLoadingRegistration(active); return null;}
/** * The loading line of an application shell: the frame stands for the * content column, the line hangs on its top edge. Two sources wait for two * and four seconds; the line stays until both are done. In an application * a Suspense boundary reports with `AppLoadingSignal` as its fallback. */export default function AppLoadingBarExample() { const [first, setFirst] = useState(false); const [second, setSecond] = useState(false); const start = () => { setFirst(true); setSecond(true); window.setTimeout(() => setFirst(false), 2000); window.setTimeout(() => setSecond(false), 4000); }; return ( <AppLoadingProvider> <div className="relative w-full max-w-xl overflow-hidden rounded-md border"> <AppLoadingBar label="Loading" /> <Wait active={first} /> <Wait active={second} /> <div className="grid gap-3 p-6"> <Button variant="outline" className="justify-self-start" disabled={first || second} onClick={start} > Start two waits </Button> <p className="text-muted-foreground text-sm"> Waiting: {[first, second].filter(Boolean).length} </p> </div> </div> </AppLoadingProvider> );}The pieces#
App: holds the count of waits; the shell wraps it around the content column.Loading Provider App: the line, placed once by the shell, absolutely positioned at the top of its nearest positioned ancestor, so nothing shifts when it appears.Loading Bar App: theLoading Signal fallbackof a Suspense boundary; it draws nothing and reports a wait while it is mounted.use: reports a wait from any other source whileApp Loading Registration(active) active, such as a link that is navigating.
The line stays while at least one source waits, and appears only after 200 ms. Outside a provider, the signal and the hook do nothing.
Usage#
import { AppLoadingBar, AppLoadingProvider, AppLoadingSignal,} from '@workspace/active-ui/components/composed/app-loading-bar';
<AppLoadingProvider> <main className="relative"> <AppLoadingBar label="Loading" /> <Suspense fallback={<AppLoadingSignal />}>{children}</Suspense> </main></AppLoadingProvider>;What your application provides#
The placement: a positioned content column, and the provider around it.
The sources of waiting, and the translated
label.The line's colour follows
--grid-where your theme defines it, else the primary colour.primary
Accessibility#
The line is an indeterminate progressbar named by label. It is not a live region: it appears on
every wait, and an announcement each time would be noise. It does not move focus.
API#
AppLoadingProvider
Props
- Type
ReactNode
The content column, including the line and every source of waiting.
Takes only the props listed.
AppLoadingBar
Props
- Type
string
The translated name of the loading state, for example t('common..
Required: a bar without a name is a nameless progressbar to a screen reader. The text is not visible -- the line itself is the visible signal -- and comes from the caller, because active- does not translate.
- Type
string
Placement, only for the shell that mounts this component. A page does not touch this prop: it does not render the line at all.
Takes only the props listed.
Parts that take only their elementโs attributes
App: no props.Loading Signal
Functions, hooks and constants
- Type
(active: boolean) => void
Reports to the shell that something is being waited for, as long as active is true.
The underlying form of App, exported separately for sources that are not a Suspense boundary. Those exist: a route change in the App Router shows no fallback -- the router keeps the old page in place until the new payload has arrived -- so without a second source the line would never appear when clicking in the sidebar. Hook it to use of each navigation link as well.
Outside an App the hook does nothing and does not throw: a boundary that accidentally sits outside the shell must not produce an error.
Types
The props of App, each described in its prop list on this page.
interface AppLoadingBarProps {
label: string;
className?: string;
}