Status Message

Shows the loading, empty, or failed state of a content area, with an optional next action. Inline layout uses the surrounding area’s existing boundary; panel adds a boundary, and fill centers a whole-view state. Loading and empty states use a live status region; errors use an alert. Use Callout for static guidance and FormField for errors tied to a specific input.

Empty state

One empty state and its next action.

No notes yet

Code

<link
  rel="component"
  href="@threadlabs/looma/components/ui-status-message/ui-status-message.html"
>
<link
  rel="component"
  href="@threadlabs/looma/components/ui-button/ui-button.html"
>

<ui-status-message
  heading="No notes yet"
>
  <ui-button
    slot="action"
    size="sm"
    preset="primary"
  >Create a note</ui-button>
</ui-status-message>
<script setup>
  import { UiButton, UiStatusMessage } from "@threadlabs/looma";
</script>

<template>
  <UiStatusMessage
    heading="No notes yet"
  >
    <template #action>
      <UiButton
        size="sm"
        preset="primary"
      >Create a note</UiButton>
    </template>
  </UiStatusMessage>
</template>
import { UiButton, UiStatusMessage } from "@threadlabs/looma";

export default function Example() {
  return <UiStatusMessage
    heading="No notes yet"
    slots={{
      action: <UiButton
        size="sm"
        preset="primary"
      >Create a note</UiButton>,
    }}
  />;
}
/** @jsxImportSource preact */
import { UiButton, UiStatusMessage } from "@threadlabs/looma";

export default function Example() {
  return <UiStatusMessage
    heading="No notes yet"
    slots={{
      action: <UiButton
        size="sm"
        preset="primary"
      >Create a note</UiButton>,
    }}
  />;
}
<script>
  import { UiButton, UiStatusMessage } from "@threadlabs/looma";
</script>

{#snippet action()}
  <UiButton
    size="sm"
    preset="primary"
  >Create a note</UiButton>
{/snippet}

<UiStatusMessage
  heading="No notes yet"
  slots={{ action }}
/>
import { Component } from "@angular/core";
import { UiButton, UiStatusMessage } from "@threadlabs/looma";

@Component({
  selector: "example-view",
  standalone: true,
  imports: [UiStatusMessage, UiButton],
  template: `
    <div
      ui-status-message
      [heading]="'No notes yet'"
    >
      <ng-container ngProjectAs="[slot='action']">
        <ng-template
          ui-button
          [size]="'sm'"
          [preset]="'primary'"
        >Create a note</ng-template>
      </ng-container>
    </div>
  `,
})
export default class Example {}

Empty, loading, and error

Inline states inside existing cards. The card supplies the boundary; the status announces its state and offers a next step.

No pages yet Pages you create appear here.<ui-button slot="action" size="sm" variant="solid"

Create a page

Loading pages…
Activity could not be loaded.

Code

<link
  rel="component"
  href="@threadlabs/looma/components/ui-stack/ui-stack.html"
>
<link
  rel="component"
  href="@threadlabs/looma/components/ui-card/ui-card.html"
>
<link
  rel="component"
  href="@threadlabs/looma/components/ui-status-message/ui-status-message.html"
>
<link
  rel="component"
  href="@threadlabs/looma/components/ui-button/ui-button.html"
>

<ui-stack
  gap="s"
>
  <ui-card>
    <ui-status-message
      heading="No pages yet"
    >
      Pages you create appear here.
      <ui-button
        slot="action"
        size="sm"
        variant="solid"
      >Create a page</ui-button>
    </ui-status-message>
  </ui-card>
  <ui-card>
    <ui-status-message
      kind="loading"
    >Loading pages…</ui-status-message>
  </ui-card>
  <ui-card>
    <ui-status-message
      kind="error"
    >Activity could not be loaded.</ui-status-message>
  </ui-card>
</ui-stack>
<script setup>
  import { UiButton, UiCard, UiStack, UiStatusMessage } from "@threadlabs/looma";
</script>

<template>
  <UiStack
    gap="s"
  >
    <UiCard>
      <UiStatusMessage
        heading="No pages yet"
      >Pages you create appear here.<template #action>
          <UiButton
            size="sm"
            variant="solid"
          >Create a page</UiButton>
        </template></UiStatusMessage>
    </UiCard>
    <UiCard>
      <UiStatusMessage
        kind="loading"
      >Loading pages…</UiStatusMessage>
    </UiCard>
    <UiCard>
      <UiStatusMessage
        kind="error"
      >Activity could not be loaded.</UiStatusMessage>
    </UiCard>
  </UiStack>
</template>
import { UiButton, UiCard, UiStack, UiStatusMessage } from "@threadlabs/looma";

export default function Example() {
  return <UiStack
    gap="s"
  >
    <UiCard>
      <UiStatusMessage
        heading="No pages yet"
        slots={{
          action: <UiButton
            size="sm"
            variant="solid"
          >Create a page</UiButton>,
        }}
      >Pages you create appear here.</UiStatusMessage>
    </UiCard>
    <UiCard>
      <UiStatusMessage
        kind="loading"
      >Loading pages…</UiStatusMessage>
    </UiCard>
    <UiCard>
      <UiStatusMessage
        kind="error"
      >Activity could not be loaded.</UiStatusMessage>
    </UiCard>
  </UiStack>;
}
/** @jsxImportSource preact */
import { UiButton, UiCard, UiStack, UiStatusMessage } from "@threadlabs/looma";

export default function Example() {
  return <UiStack
    gap="s"
  >
    <UiCard>
      <UiStatusMessage
        heading="No pages yet"
        slots={{
          action: <UiButton
            size="sm"
            variant="solid"
          >Create a page</UiButton>,
        }}
      >Pages you create appear here.</UiStatusMessage>
    </UiCard>
    <UiCard>
      <UiStatusMessage
        kind="loading"
      >Loading pages…</UiStatusMessage>
    </UiCard>
    <UiCard>
      <UiStatusMessage
        kind="error"
      >Activity could not be loaded.</UiStatusMessage>
    </UiCard>
  </UiStack>;
}
<script>
  import { UiButton, UiCard, UiStack, UiStatusMessage } from "@threadlabs/looma";
</script>

{#snippet action()}
  <UiButton
    size="sm"
    variant="solid"
  >Create a page</UiButton>
{/snippet}

<UiStack
  gap="s"
>
  <UiCard>
    <UiStatusMessage
      heading="No pages yet"
      slots={{ action }}
    >Pages you create appear here.</UiStatusMessage>
  </UiCard>
  <UiCard>
    <UiStatusMessage
      kind="loading"
    >Loading pages…</UiStatusMessage>
  </UiCard>
  <UiCard>
    <UiStatusMessage
      kind="error"
    >Activity could not be loaded.</UiStatusMessage>
  </UiCard>
</UiStack>
import { Component } from "@angular/core";
import { UiButton, UiCard, UiStack, UiStatusMessage } from "@threadlabs/looma";

@Component({
  selector: "example-view",
  standalone: true,
  imports: [UiStack, UiCard, UiStatusMessage, UiButton],
  template: `
    <div
      ui-stack
      [gap]="'s'"
    >
      <ng-template
        ui-card
      >
        <div
          ui-status-message
          [heading]="'No pages yet'"
        >Pages you create appear here.<ng-container ngProjectAs="[slot='action']">
            <ng-template
              ui-button
              [size]="'sm'"
              [variant]="'solid'"
            >Create a page</ng-template>
          </ng-container></div>
      </ng-template>
      <ng-template
        ui-card
      >
        <div
          ui-status-message
          [kind]="'loading'"
        >Loading pages…</div>
      </ng-template>
      <ng-template
        ui-card
      >
        <div
          ui-status-message
          [kind]="'error'"
        >Activity could not be loaded.</div>
      </ng-template>
    </div>
  `,
})
export default class Example {}

Panel

layout="panel" for an empty area within a page, such as a feature that is not set up yet.

No connections yet Connect a tool to bring its pages in.

Code

<link
  rel="component"
  href="@threadlabs/looma/components/ui-status-message/ui-status-message.html"
>

<ui-status-message
  layout="panel"
  heading="No connections yet"
>Connect a tool to bring its pages in.</ui-status-message>
<script setup>
  import { UiStatusMessage } from "@threadlabs/looma";
</script>

<template>
  <UiStatusMessage
    layout="panel"
    heading="No connections yet"
  >Connect a tool to bring its pages in.</UiStatusMessage>
</template>
import { UiStatusMessage } from "@threadlabs/looma";

export default function Example() {
  return <UiStatusMessage
    layout="panel"
    heading="No connections yet"
  >Connect a tool to bring its pages in.</UiStatusMessage>;
}
/** @jsxImportSource preact */
import { UiStatusMessage } from "@threadlabs/looma";

export default function Example() {
  return <UiStatusMessage
    layout="panel"
    heading="No connections yet"
  >Connect a tool to bring its pages in.</UiStatusMessage>;
}
<script>
  import { UiStatusMessage } from "@threadlabs/looma";
</script>

<UiStatusMessage
  layout="panel"
  heading="No connections yet"
>Connect a tool to bring its pages in.</UiStatusMessage>
import { Component } from "@angular/core";
import { UiStatusMessage } from "@threadlabs/looma";

@Component({
  selector: "example-view",
  standalone: true,
  imports: [UiStatusMessage],
  template: `
    <div
      ui-status-message
      [layout]="'panel'"
      [heading]="'No connections yet'"
    >Connect a tool to bring its pages in.</div>
  `,
})
export default class Example {}

API

Package: @threadlabs/looma · Native root: div

Attributes

NameDescriptionPropertyTypeDefaultAllowed values
headingA short heading above the message, such as "No pages yet". Optional.headingstring—
kindWhat the view is saying. empty: there is nothing here yet (announced politely); loading: it is on its way, with a spinner (announced politely); error: it could not be shown, in the danger tone (announced at once).kindkeywordemptyempty, loading, error
layoutWhere it sits. inline takes its place in the view's flow; panel sits it on a dashed surface, for an empty area in a page; fill centres it in all the space it is given, for a whole view that is loading or failed.layoutkeywordinlineinline, panel, fill

Framework props

NameTypeDefaultAllowed values
headingstring—
kind"empty" | "loading" | "error"emptyempty, loading, error
layout"inline" | "panel" | "fill"inlineinline, panel, fill

Events

No events.

Slots

NamePurpose
defaultDefault child content.
actionNamed content for the action region.

Design tokens

Set shared theme properties on a theme boundary. Component hooks apply only to their owning component.

Component tokens

No component tokens.

Shared tokens

TokenFallback
--ui-border-strong—
--ui-border-width—
--ui-danger-subtle-text—
--ui-font-semibold—
--ui-font-size-md—
--ui-line-height—
--ui-radius-lg—
--ui-space-1—
--ui-space-2—
--ui-space-4—
--ui-space-6—
--ui-text-muted—
--ui-text-primary—