> ## Documentation Index
> Fetch the complete documentation index at: https://docs.twenty.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Loader

> Display a compact animated loading indicator beside a status label.

export const StoryEmbed = ({storyId, title, height = 120}) => <>
    <Tabs>
      <Tab title="Light">
        <iframe title={`${title} (light)`} src={`https://storybook.twenty.com/iframe.html?id=${storyId}&viewMode=story&globals=colorScheme:light`} width="100%" height={height} loading="lazy" allow="clipboard-write" style={{
  border: 0
}} />
      </Tab>
      <Tab title="Dark">
        <iframe title={`${title} (dark)`} src={`https://storybook.twenty.com/iframe.html?id=${storyId}&viewMode=story&globals=colorScheme:dark`} width="100%" height={height} loading="lazy" allow="clipboard-write" style={{
  border: 0
}} />
      </Tab>
    </Tabs>
    <a href={`https://storybook.twenty.com/?path=/story/${storyId}`}>
      Open in Storybook
    </a>
  </>;

`Loader` shows an animated dot inside a small bordered capsule. Use it while an operation is in progress and the percentage is unknown. For numeric progress, use [ProgressBar](/ui/primitives/feedback/progress-bar).

<StoryEmbed storyId="ui-feedback-loader--display" title="Loader example" height={90} />

## Usage

```tsx theme={null}
import { Loader } from 'twenty-ui/primitives/feedback';
import { Text } from 'twenty-ui/primitives/typography';

type SaveFeedbackProps = { isSaving: boolean };

export const SaveFeedback = ({ isSaving }: SaveFeedbackProps) => (
  <Text
    render={<div />}
    role="status"
    style={{ display: 'flex', alignItems: 'center', gap: 8 }}
  >
    {isSaving && <Loader color="blue" aria-hidden="true" render={<span />} />}
    {isSaving ? 'Saving changes' : 'Changes saved'}
  </Text>
);
```

Place visible status text beside the loader in a wrapper. The loader is a fixed-size visual indicator and does not accept children.

The application owns loading state. Keep the status wrapper mounted so text updates can be announced. The loader assigns no label or live-region role by default. Use `aria-hidden="true"` when surrounding text or a button already supplies loading feedback.

## Native composition and labeling

Native props, `className`, `style`, and `ref` target the container div. `render` accepts an element or callback through Base UI composition. Styles override color defaults. `LoaderProps` is exported from the feedback family.

For a standalone indicator, provide an explicit status role and accessible name. The animated dot remains decorative.

```tsx theme={null}
import { Loader, type LoaderProps } from 'twenty-ui/primitives/feedback';

const loadingProps: LoaderProps = {
  role: 'status',
  'aria-label': 'Loading results',
  color: 'blue',
};

export const LoadingResults = () => (
  <Loader {...loadingProps} render={<span />} />
);
```

Use `aria-labelledby` instead when visible text names the status. Keep a live-region owner mounted when it needs to announce changing text. The application owns when feedback appears and what it says.

## Color

Pass a theme palette color to set both the border and dot. Without a color, the loader inherits a surrounding button color when available, otherwise it uses the tertiary text color. For a button action, prefer [Button's loading state](/ui/primitives/input/button#state-ownership), which keeps the feedback inside the control.

## Props

<ParamField body="aria-hidden" type="Booleanish">
  Hide a decorative loader when another element supplies loading feedback.
</ParamField>

<ParamField body="aria-label" type="string">
  Accessible name for a labeled loader status.
</ParamField>

<ParamField body="aria-labelledby" type="string">
  ID of text that names the loader status.
</ParamField>

<ParamField body="className" type="string">
  CSS class merged with the loader container class.
</ParamField>

<ParamField body="color" type="&#x22;amber&#x22; | &#x22;blue&#x22; | &#x22;bronze&#x22; | &#x22;brown&#x22; | &#x22;crimson&#x22; | &#x22;cyan&#x22; | &#x22;gold&#x22; | &#x22;grass&#x22; | &#x22;gray&#x22; | &#x22;green&#x22; | &#x22;iris&#x22; | &#x22;jade&#x22; | &#x22;lime&#x22; | &#x22;mint&#x22; | &#x22;orange&#x22; | &#x22;pink&#x22; | &#x22;plum&#x22; | &#x22;purple&#x22; | &#x22;red&#x22; | &#x22;ruby&#x22; | &#x22;sky&#x22; | &#x22;tomato&#x22; | &#x22;turquoise&#x22; | &#x22;violet&#x22; | &#x22;yellow&#x22;">
  Theme color for the animated dot and border. When omitted, inherits the button color if available, otherwise the tertiary text color.
</ParamField>

<ParamField body="ref" type="Ref<HTMLDivElement>">
  Ref to the container div, or the element supplied through render.
</ParamField>

<ParamField body="render" type="ReactElement<unknown, string | JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, {}>">
  Element or render callback composed through Base UI useRender.
</ParamField>

<ParamField body="role" type="AriaRole">
  Supply status to expose loading feedback as a live region. No role is assigned by default.
</ParamField>

<ParamField body="style" type="CSSProperties">
  Native styles merged with the loader color defaults. Explicit styles win.
</ParamField>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.