<script setup lang="ts">
import { ContentLoader } from "@dicehub/kappa/components/content-loader";
import { ref } from "vue";
const state = ref<"ready" | "loading" | "empty" | "error">("loading");
async function loadCases() {
state.value = "loading";
// Load data in your application, then set ready, empty, or error.
}
</script>
<template>
<ContentLoader :state="state" loading-label="Loading cases…"
empty-title="No cases yet" retryable @retry="loadCases">
<CaseList :items="cases" />
</ContentLoader>
</template>Installation
import { ContentLoader } from "@dicehub/kappa";
// Or: "@dicehub/kappa/components/content-loader"Composition
Content Loader composes Kappa Loader, Empty, and Button. The application sets one state and owns data requests, cancellation, and retries. The component does not fetch data or infer whether a response is empty.
The default slot renders only in the ready state. With keep-mounted, content mounts on the first ready state and remains hidden during later loading, empty, or error states. Controls inside that hidden region cannot receive keyboard focus. Close any open dialogs or dropdowns before changing state: their portalled content lives outside the hidden region. Avoid this option when keeping a large view mounted is expensive.
Examples
Compact
Use a shorter placeholder inside side panels and small cards.
<ContentLoader state="loading" compact loading-label="Loading cases…" />Custom states
Replace each state with a skeleton, guidance, or recovery controls.
<ContentLoader :state="state" @retry="loadCases">
<template #loading><SkeletonLine /></template>
<template #empty>
<p>No cases match the current filter.</p>
<Button @click="clearFilter">Clear filter</Button>
</template>
<template #error="{ retry }">
<p>The case service is unavailable.</p>
<Button @click="retry">Reconnect</Button>
</template>
<CaseList :items="cases" />
</ContentLoader>Preserve mounted content
Write a note, then refresh. The input DOM node and its local state remain intact.
<ContentLoader :state="state" keep-mounted>
<Input v-model="note" aria-label="Run note" />
</ContentLoader>Accessibility
The content region exposes aria-busy while loading. A separate, persistent status region announces the current state without moving focus. Default labels can be translated with props. Custom slots reuse these announcements; avoid a second live region with the same message.
When a refresh removes a focused control, the application should move focus to a stable control. Loader respects reduced-motion preferences. Empty and error states use no animation.
API Reference
state: ready | loading | empty | error, default ready. compact, keepMounted, and retryable default to false. Native attributes and listeners reach the root.
Text props: loadingLabel (“Loading…”), emptyTitle (“No results”), emptyDescription (“There is no content to show.”), errorTitle (“Unable to load content”), errorDescription (“Try again in a moment.”), and retryLabel (“Try again”).
Slots: default (ready content), loading, empty, and error. The error slot receives retry(), which emits the retry event. The default retry button appears only with retryable. Set the next state in the application handler.
Set --kappa-content-loader-min-height to reserve placeholder space. The default is 10rem, or 6rem with compact spacing. Ready content uses its natural height.
Exports: ContentLoader, ContentLoaderProps, ContentLoaderState, ContentLoaderSlots, ContentLoaderEmits, and CONTENT_LOADER_STATES.