Skip to content
Sveltier
Mode
Core

Defaults

Set props once for a whole subtree instead of on every component: globally, per component, and nested where a section needs something different.

How a prop resolves

For every defaultable prop, a component takes the first value that is set:

  1. the prop passed to the component;
  2. the nearest DefaultsProvider's defaults for that component;
  3. that provider's global defaults;
  4. the component's built-in value.

undefined and null count as "not set". Every step is reactive: changing a prop or a provider's defaults updates the components below it.

Global defaults

global takes the shared props variant, density, size, color, rounded and disabled. A global default only reaches components that have the prop, so it never adds an attribute to an element that does not expect it. Set the app-wide ones on UIProvider:

+layout.svelte
<script>
  import { UIProvider } from 'sveltier';
  import { monolith } from 'sveltier/themes/monolith';
  let { children } = $props();
</script>

<UIProvider themes={monolith} defaults={{ global: { density: 'compact', rounded: 'lg' } }}>
  {@render children()}
</UIProvider>

Nesting

A DefaultsProvider changes the defaults of a section, and providers nest. An inner provider is merged over its parent key by key, so it only needs the values it changes.

Nested providers
<DefaultsProvider defaults={{ global: { size: 'sm' } }}>
  <Toolbar />

  <!-- Inner values win, key by key: size becomes lg, PriceTag gets its colour. -->
  <DefaultsProvider defaults={{ global: { size: 'lg' }, PriceTag: { color: 'up' } }}>
    <Hero />
  </DefaultsProvider>
</DefaultsProvider>

Your own components

The same mechanism is public. Register your component's defaults by declaration merging, so DefaultsConfig type-checks its key and values:

app.d.ts
// src/app.d.ts, or any .d.ts file in your project
import type { Color, Size } from 'sveltier';

declare module 'sveltier' {
  interface ComponentDefaults {
    PriceTag: { size: Size; color: Color; showSign: boolean };
  }
}

Then resolve its props with useDefaults. Destructure the defaultable props without fallbacks and resolve them in $derived: a fallback in the destructuring would hide the provider's value, and on older Svelte 5 releases it is not reactive at all.

PriceTag.svelte
<script lang="ts">
  import { useDefaults, type Color, type ComponentDefaults, type Size } from 'sveltier';

  interface Props {
    value: number;
    size?: Size;
    color?: Color;
    showSign?: boolean;
  }

  // No fallbacks in the destructuring: a fallback would hide the provider's defaults.
  let { value, size, color, showSign }: Props = $props();

  const resolve = useDefaults<ComponentDefaults['PriceTag']>('PriceTag', {
    size: 'md',
    color: 'neutral',
    showSign: true
  });
  const p = $derived(resolve({ size, color, showSign }));
</script>

<span class="price-tag price-tag--{p.size}">
  {p.showSign && value > 0 ? '+' : ''}{value}
</span>

See DefaultsProvider and useDefaults for the full API.