Skeleton is comprised of three pillars - the design system, our extensions to Tailwind, and an optional suite of framework-specific components. Together these form a comprehensive solution for designing and implementing complex web interfaces at scale.
Design System
Explore each pillar of the Skeleton design system. Provided via the Skeleton core.
Tailwind Components
Tailwind components that act as primitives for creating complex interfaces. Provided via the Skeleton core.
Framework Components
Skeleton also offers optional component packages for select component frameworks. Each component automatically adapts to Skeleton’s design system. While still allowing a high level of customization.
Supported Frameworks
| Framework | NPM Package | Description |
|---|---|---|
| React | @skeletonlabs/skeleton-react | Contains all React components. |
| Svelte | @skeletonlabs/skeleton-svelte | Contains all Svelte components. |
Powered by Zag.js
Skeleton’s components are built on Zag.js, which provides a collection of framework-agnostic UI component patterns to manage logic and state. Zag is actively maintained by industry veterans, such as Segun Adebayo - the creator and core maintainer for Chakra UI , Ark UI , and PandaCSS .
View Zag.js
Importing Components
You may import components per each Skeleton framework as follows.
import { Avatar } from '@skeletonlabs/skeleton-react';import { Avatar } from '@skeletonlabs/skeleton-svelte';This also includes access to the component prop types.
import type { AvatarRootProps, ... } from '@skeletonlabs/skeleton-react';import type { AvatarRootProps, ... } from '@skeletonlabs/skeleton-svelte';Composed Pattern
Skeleton components are granular. This offers direct access to all children within the tree, similar to working with raw HTML. This allows passing in arbitrary props and attributes directly to the template within. Including: required, data-*, style, className, and more.
export default function Avatar() {
return (
<Avatar>
<Avatar.Image src="https://i.pravatar.cc/150?img=48" />
<Avatar.Fallback>SK</Avatar.Fallback>
</Avatar>
);
}Skeleton components are granular. This offers direct access to all children within the tree, similar to working with raw HTML. This allows passing in arbitrary props and attributes directly to the the template within. Including: required, data-*, style, class, and more.
<Avatar>
<Avatar.Image src="https://i.pravatar.cc/150?img=48" />
<Avatar.Fallback>SK</Avatar.Fallback>
</Avatar>Styling Components
Skeleton components implement a universal convention for accepting CSS utility classes via the className attribute. Use this to pass any CSS utility class.
export default function Avatar() {
return (
<Avatar className="rounded-2xl">
<Avatar.Image src="https://i.pravatar.cc/150?img=48" className="grayscale" />
<Avatar.Fallback>SK</Avatar.Fallback>
</Avatar>
);
}Skeleton components implement a universal convention for accepting CSS utility classes via the class attribute. Use this to pass any CSS utility class.
<Avatar class="rounded-2xl">
<Avatar.Image src="https://i.pravatar.cc/150?img=48" class="greyscale" />
<Avatar.Fallback>SK</Avatar.Fallback>
</Avatar>By default, all internal styles are auto-prefixed to ensure they are assigned to the @base layer. This ensures any classes you pass through the className attribute are automatically given precedence. No mental overhead, it just works.
By default, all internal styles are auto-prefixed to ensure they are assigned to the @base layer. This ensures any classes you pass through the class attribute are automatically given precedence. No mental overhead, it just works.
@custom-variant skb {
@layer base {
@slot;
}
}Extensible Markup
Skeleton components provide a mechanism for overwriting the internal HTML with custom markup. Use the element prop to provide a custom element, this prop accepts a function which the attributes are passed into. Then spread the attributes to your custom elements. Note that this is an optional and advanced feature aimed at power users, and should not be needed for normal usage.
export default function () {
return (
<Accordion>
<Accordion.Item value="item-1">
<h3>
<Accordion.ItemTrigger element={(attributes) => <button {...attributes}>My Own Button</button>} />
</h3>
<Accordion.ItemContent>Content for Item 1</Accordion.ItemContent>
</Accordion.Item>
</Accordion>
);
}<Accordion>
<Accordion.Item value="item-1">
<h3>
<Accordion.ItemTrigger>
{#snippet element(attributes)}
<button {...attributes}>My Own Button</button>
{/snippet}
</Accordion.ItemTrigger>
</h3>
<Accordion.ItemContent>Content for Item 1</Accordion.ItemContent>
</Accordion.Item>
</Accordion>Custom Animations
Using the extensible markup pattern, you may implement custom animations. We showcase this below with Motion , but you could also use framework agnostic solutions such as Anime.js or Animate.css .
import { Accordion } from '@skeletonlabs/skeleton-react';
import { motion, AnimatePresence } from 'motion/react';
export default function CustomAnimation() {
return (
<Accordion>
{['1', '2', '3'].map((item) => (
<Accordion.Item key={item} value={item}>
<h3>
<Accordion.ItemTrigger>Item {item}</Accordion.ItemTrigger>
</h3>
<Accordion.ItemContent
element={(attributes) => (
<AnimatePresence initial={false}>
{!attributes.hidden && (
<motion.div
className="overflow-hidden"
initial={{ height: 0, opacity: 0 }}
animate={{ height: 'auto', opacity: 1 }}
exit={{ height: 0, opacity: 0 }}
>
<div {...attributes}>Content for item {item}</div>
</motion.div>
)}
</AnimatePresence>
)}
/>
</Accordion.Item>
))}
</Accordion>
);
}- Implement the
elementsnippet to gain access to theattributes. - Spread the
attributesto the custom element, a<div>in this example. - Wrap the custom element in Motion’s
<AnimatePresence>. - Then implement conditional rendering that triggers animations when
attributes.hiddenis toggled.
Using the extensible markup pattern, you may implement custom animations. We showcase this below with Svelte Transitions , but you could also use framework agnostic solutions such as Motion , Anime.js , or Animate.css .
<script lang="ts">
import { Accordion } from '@skeletonlabs/skeleton-svelte';
import { slide } from 'svelte/transition';
</script>
<Accordion>
{#each ['1', '2', '3'] as item (item)}
<Accordion.Item value="item-{item}">
<h3>
<Accordion.ItemTrigger>Item {item}</Accordion.ItemTrigger>
</h3>
<Accordion.ItemContent>
{#snippet element(attributes)}
{#if !attributes.hidden}
<div {...attributes} transition:slide>Content for item {item}</div>
{/if}
{/snippet}
</Accordion.ItemContent>
</Accordion.Item>
{/each}
</Accordion>- Implement the
elementsnippet to gain access to theattributes. - Spread the
attributesto the custom element, a<div>in this example. - Add the
transition:slideand configure your preferred options. - Then implement the wrapping
#ifblock that triggers transitions whenattribute.hiddenis toggled.
Provider Pattern
Most Skeleton components also support the Provider Pattern. This utilizes a provider component that replaces the root and provides access to the underlying component APIs. In practice, this allows direct access to Zag.js API features, such as programmatic control for overlay components, the ability to clear input components, and more.
import { Portal, Tooltip, useTooltip } from '@skeletonlabs/skeleton-react';
export default function TooltipExample() {
const tooltip = useTooltip();
return (
<>
<button type="button" onClick={() => tooltip.setOpen(!tooltip.open)}>
Trigger
</button>
<Tooltip.Provider value={tooltip}>
<Tooltip.Trigger>Anchor</Tooltip.Trigger>
<Portal>
<Tooltip.Positioner>
<Tooltip.Content>Content</Tooltip.Content>
</Tooltip.Positioner>
</Portal>
</Tooltip.Provider>
</>
);
}<script lang="ts">
import { Portal, Tooltip, useTooltip } from '@skeletonlabs/skeleton-svelte';
const id = $props.id();
const tooltip = useTooltip({ id });
</script>
<button type="button" onclick={() => tooltip().setOpen(!tooltip().open)}>Trigger</button>
<Tooltip.Provider value={tooltip}>
<Tooltip.Trigger>Anchor</Tooltip.Trigger>
<Portal>
<Tooltip.Positioner>
<Tooltip.Content>Content</Tooltip.Content>
</Tooltip.Positioner>
</Portal>
</Tooltip.Provider>Note: Svelte requires passing
idbecause$props.idis not available outside the component’s<script>block.
Learn More
For a comprehensive guide to how Skeleton implements components, refer to our contribution guidelines .