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.
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.
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.
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
| Name | Description | Property | Type | Default | Allowed values |
|---|---|---|---|---|---|
heading | A short heading above the message, such as "No pages yet". Optional. | heading | string | | — |
kind | What 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). | kind | keyword | empty | empty, loading, error |
layout | Where 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. | layout | keyword | inline | inline, panel, fill |
Framework props
| Name | Type | Default | Allowed values |
|---|---|---|---|
heading | string | | — |
kind | "empty" | "loading" | "error" | empty | empty, loading, error |
layout | "inline" | "panel" | "fill" | inline | inline, panel, fill |
Events
No events.
Slots
| Name | Purpose |
|---|---|
default | Default child content. |
action | Named 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
| Token | Fallback |
|---|---|
--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 | — |