Components
Back to catalog
Component detail entry
General
@vean/ui
SConfigProvider

ConfigProvider

ConfigProvider belongs to the General category. Start with demos and usage, then use the API tables to finish integration and styling decisions.

Use the quick jump panel to reach overview, usage, demos, and API first, then continue through the full page.

Quick jump

Related components

If you are composing interactions in the same family, these components usually pair well together.

ConfigProvider

Overview

The SConfigProvider component is the root configuration provider for the VeanUI library. It manages global themes, localization, icon settings, and other context-aware features. It should wrap your entire application or specific sections that require isolated configuration.

Features

  • 🎨 Theme System: Configure global colors and radius via theme prop.
  • 📏 Size Control: Manage global component sizing (xs, sm, md, lg, xl, 2xl).
  • 🖼️ Icon Configuration: Set default width and height for all SIcon components.
  • ⏳ Loading Bar Integration: Configure the global top loading bar.
  • 🍞 Toast Integration: Configure global toast settings.
  • 🌐 Direction: Support for LTR/RTL layouts.
  • 🌍 Locale: Drive built-in component messages via locale and messages props.

Basic Usage

Wrap your application root with SConfigProvider.

<script setup lang="ts">
import { SConfigProvider } from '@vean/ui';
</script>

<template>
  <SConfigProvider
    :theme="{
      base: 'gray',
      primary: 'violet',
      radius: '0.625rem',
      size: 'md'
    }"
    :iconify="{ width: '1.25em', height: '1.25em' }"
    dir="ltr"
  >
    <App />
  </SConfigProvider>
</template>

Advanced Theme Configuration

You can fully customize the theme using the theme prop. more details can be found in the theming documentation.

Direction / RTL

Use the dir prop to switch the supported components between left-to-right and right-to-left layouts.

<template>
  <SConfigProvider dir="rtl">
    <App />
  </SConfigProvider>
</template>

Set dir="ltr" explicitly if you want to force left-to-right behavior inside a subtree.

Locale / Internationalization

VeanUI ships with built-in component message sets for the following locales. These messages drive component-level text such as aria-labels and empty-state copy.

CodeLanguage
zh-CNSimplified Chinese
zh-TWTraditional Chinese
enEnglish
arArabic
jaJapanese
koKorean
deGerman
frFrench
esSpanish
pt-BRPortuguese (Brazil)
ruRussian
trTurkish
idIndonesian

Only en and zh-CN are pre-registered by default. Other supported locale files can be imported from @vean/aria/locale/{code} and registered manually.

When dir is omitted, ConfigProvider automatically follows the direction implied by locale. For example, locale="ar" resolves to dir="rtl", while locale="en" resolves to dir="ltr". Pass dir explicitly if you need to override that mapping.

Switching to a pre-registered locale

Pass the locale code directly to SConfigProvider:

<SConfigProvider locale="zh-CN">
  <!-- Built-in component copy now displays in Simplified Chinese -->
</SConfigProvider>

Loading another supported locale

registerLocale accepts two forms:

  • registerLocale(localeRegistry) for built-in locale files or any custom registry object when you need explicit name and dir metadata.
  • registerLocale(key, messages) for a lightweight custom locale keyed only by its message table.

Import the locale file as a default export, register it once during app setup, and then pass the same locale code to SConfigProvider:

import { registerLocale } from '@vean/aria/locale';
import ar from '@vean/aria/locale/ar';

registerLocale(ar);
<SConfigProvider locale="ar">
  <!-- Component copy now displays in Arabic and dir defaults to rtl -->
</SConfigProvider>

Supported locale files are also available at @vean/aria/locale/{code} if you want to extend one as the base for your own custom locale.

Overriding individual messages

Use the messages prop to replace only the keys you need. Keys not listed fall back to the selected built-in locale.

<script setup lang="ts">
import type { LocaleMessagesOverrides } from '@vean/aria';

const messages: LocaleMessagesOverrides = {
  table: {
    emptyTitle: 'Nothing here yet',
    emptyDescription: 'Add your first item to get started.'
  }
};
</script>

<template>
  <SConfigProvider locale="en" :messages="messages">
    <!-- Table empty state now shows custom copy -->
  </SConfigProvider>
</template>

Using a custom locale from scratch

Import en as a base registry, override any keys from en.messages, then register under a custom locale key with the shorthand form:

import { registerLocale, en } from '@vean/aria/locale';
import type { LocaleMessages } from '@vean/aria/locale';

const myLocale: LocaleMessages = {
  ...en.messages,
  pagination: {
    ...en.messages.pagination,
    nextPage: 'Next →',
    prevPage: '← Prev'
  }
};

registerLocale('custom', myLocale);

Then pass locale="custom" to SConfigProvider. If you also need a custom display name or explicit dir, pass a full LocaleRegistry object to registerLocale(...) instead.

Locale key reference

pagination

KeyDefault (en)Description
firstPageFirst pageAria-label and default slot text for the first-page button
prevPagePrevious pageAria-label and default slot text for the previous-page button
nextPageNext pageAria-label and default slot text for the next-page button
lastPageLast pageAria-label and default slot text for the last-page button

table

KeyDefault (en)PlaceholdersDescription
emptyTitleNo data—Title in the default empty slot
emptyDescriptionThere is no data to display.—Description in the default empty slot
selectAllRowsSelect all rows—Aria-label for the header “select all” checkbox
sortByColumnSort by {column}{column}Sort button label with no active sort
sortByColumnAscSort by {column}, currently ascending{column}Sort button label when ascending
sortByColumnDescSort by {column}, currently descending{column}Sort button label when descending
resizeColumnResize {column} column{column}Aria-label for the column resize handle
expandRowExpand row {row}{row}Aria-label for expanding a row
collapseRowCollapse row {row}{row}Aria-label for collapsing a row
selectRowSelect row {row}{row}Aria-label for the row selection checkbox

Fallback rules

  1. A key from user-supplied messages is used first.
  2. If absent, the built-in messages for locale are used.
  3. If locale is unknown or unset, en is the final fallback.

First-batch supported components

The following components respect ConfigProvider.locale and ConfigProvider.messages in this release:

  • Pagination — navigation button labels
  • Table — empty state, sort/resize/expand/select aria-labels

Additional components will be added in future releases following the same pattern.

API

Aria Layer

1

ConfigProvider

18 Props
Props
PropTypeDefaultDescription
theme
ConfigProviderThemeOptions
—
The theme options.
iconify
IconifyOptions
—
The iconify options.
progress
Partial<ProgressProviderProps>
—
The global top progress configuration of your application.
toast
Partial<ToastProviderProps>
—
The global toast configuration of your application. This will be inherited by the related components.
customToast
boolean
false
Whether to use custom toast rendering. If set to `true`, the `ToastProvider` will not render the default toast UI, and you can import `ToastProvider` component to render custom toast UI.
persistTheme
boolean
false
Whether to enable persisted theme reading from localStorage. When disabled (default), the ConfigProvider only consumes the explicit `theme` prop and never reads or writes any storage, keeping the current behavior. When enabled, the persisted theme envelope fills in keys not explicitly provided by `theme`, and changes are written back through a single debounced writer.
themeConfig
ThemeEnvelopeInput
—
The persisted theme envelope injected from the server. Used during SSR to render the same theme the client persisted; on the client the localStorage envelope is the source of truth.
presetProvider
((name: string) => ThemePresetColors | null)
—
The server-side custom theme preset registry resolver. Maps a stored preset name to its definition so SSR can render custom presets without localStorage access. Only used when `persistTheme` is enabled on the server; on the client the presets table (carried by the theme envelope) is the source of truth.
isServer
boolean
isServerRuntime()
Whether the component is running in a server environment. The UI library is pre-built, so `import.meta.env.SSR` is baked at build time and cannot detect the consumer's runtime. This defaults to runtime detection (`typeof window === 'undefined'`), and can be overridden with the app's own flag (e.g. Nuxt's `import.meta.server`) when the app is bundled for both server and client.
getExtraTheme
(() => Record<string, unknown>)
—
Read the application's own config into the persisted theme envelope. Lets an app keep its settings in the same storage entry as the theme — one key, one debounced writer, one cross-tab sync — instead of adding a second key with a second writer. Reactive state read inside the getter is tracked, so a change re-persists without any extra wiring. The payload must be JSON-serializable. Only used when `persistTheme` is enabled.
onExtraThemeChange
((extra: Record<string, unknown>) => void)
—
Called with the extra config carried by the persisted theme envelope. Fires once on mount with the stored payload and again whenever another tab rewrites the envelope. Only fires when `persistTheme` is enabled; never on the server.
dir
Direction
—
The global reading direction of your application. This will be inherited by all components. When omitted, the direction follows the current locale and falls back to `'ltr'`.
locale
string
'en'
The global locale of your application. This will be inherited by all components.
nonce
string
—
The global `nonce` value of your application. This will be inherited by the related components.
tooltip
Partial<TooltipProviderProps>
—
The global tooltip configuration of your application. This will be inherited by the related components.
iconRender
((icon: IconValue) => VNode)
—
A function to render the icon. This is useful when you want to use a custom icon library or want to wrap the icon with a custom component.
linkComponent
Component<PropsOrInstance = any, RawBindings = any, D = any, C extends ComputedOptions = ComputedOptions, M extends MethodOptions = MethodOptions, E extends EmitsOptions | Record<string, any[]> = {}, S extends Record<string, any> = any>
—
The component used by `Link` to render internal links. This is useful when you want to integrate a framework-specific link component (e.g. Nuxt `NuxtLink`, a custom `RouterLink` wrapper, or a locale-prefixing `Link`) instead of the globally registered `RouterLink`. When omitted, `Link` resolves the global `RouterLink` and falls back to a native `<a>`.
messages
LocaleMessagesOverrides
—
Overrides for built-in component locale messages. Merged on top of the locale selected by the `locale` prop, so you only need to supply the keys you want to change.

Notes

Architecture and benchmark differences

VeanUI splits ConfigProvider into an Aria layer (@vean/aria/config-provider) that owns locale, direction, tooltip, and message context, and a styled layer (@vean/ui) that owns theme CSS injection, icon rendering, and provider composition (toast / dialog / progress). This mirrors shadcn/ui’s headless/styled separation and differs from single-package providers such as Ant Design, Element Plus, MUI, Mantine, and Naive UI.

AspectVeanUIAnt Design / Element Plus / MUI / Mantine / Naive UI
ArchitectureAria + styled split, dual provide/inject contextssingle package, single ConfigProvider
Theme injectionbuildThemeCss() (@vean/ui → @vean/theme) written into a <style id="vean-theme"> tagCSS variables / theme object / ConfigProvider.theme
Dark modetheme.darkSelector ('class' → .dark, 'media' → OS, custom); toggle .dark classtheme.dark, dark-mode class, colorScheme
RTLdir prop + useDirection; auto-derived from locale with RTL prefix fallbackdirection prop, dir attribute, theme direction
i18nlocale + messages overrides; registerLocale for additional localeslocale prop / LocalizationProvider
Provider compositionrenders ToastProvider, DialogProvider, ProgressProvider inside the default slotseparate providers mounted by the user

Runtime cautions

  • SSR: the theme never renders into the component tree — the provider owns a real <head> element (<style id="vean-theme">, created at mount or adopted from the first-paint script) and keeps its content in sync, so there is no server/client style-content mismatch. The first paint carries the correct theme because the static default block ships with the UnoCSS preset (zero-specificity :where() layers) and the init script applies the persisted snapshot before the browser paints. SIcon receives ssr: import.meta.env.SSR so icon rendering is SSR-safe.
  • Style tag lifecycle: the <style id="vean-theme"> (theme) and <style id="__Vean_Aria_Styles"> (Aria layer) tags persist in <head> for the lifetime of the page. They are reactive — changing the theme prop updates the CSS content in place. Unmounting the provider does not remove them (they are global by design).
  • Locale registration: only en and zh-CN are pre-registered. For any other locale (e.g. ar, ja, fr), import the locale file from @vean/aria/locale/{code} and call registerLocale(...) once during app setup. Direction (dir) falls back to a built-in RTL prefix map (ar, he, fa, ur, …) even before a locale is registered, so locale="ar" resolves to dir="rtl" out of the box.
  • Nesting: SConfigProvider can be nested. An inner provider overrides the outer context for its subtree. The Aria and UI contexts are independent injection keys, so Aria-only consumers (e.g. useDirection) read the Aria context while UI consumers (e.g. SIcon iconify defaults) read the UI context.

SSR theme consistency (no flash on refresh)

The theme is persisted in localStorage only (no cookie). A client-only style injection would apply the saved theme only after hydration, flashing the default theme on refresh. @vean/theme ships an SSR-safe init script (under the @vean/theme/ssr subpath) that applies the saved theme before first paint:

  • createThemeInitScript() — returns a small IIFE to inline in <head>. Before first paint it reads the theme envelope from localStorage, toggles the dark class on <html> (resolving mode: 'auto' against prefers-color-scheme), sets color-scheme, and patches the snapshot into the <style id="vean-theme"> element. The server renders the default theme; the script corrects it before the browser paints, so there is no flash. A payload written by an older vocabulary is migrated on read, and its snapshot (engine output of that older vocabulary) is dropped — the provider re-emits it on mount.
  • readThemeEnvelope() / writeThemeEnvelope() / clearThemeEnvelope() — explicit persistence helpers, plus parseThemeEnvelope() (validate/migrate a raw string), parseThemeOptions() (validate just the options) and createThemeWriter() (the debounced single writer) — all under the @vean/theme/storage subpath.

Keeping your own config in the same envelope

Pass getExtraTheme to store app settings next to the theme, and onExtraThemeChange to apply them back — same __VEAN_THEME entry, one key, one debounced writer, one cross-tab sync:

<script setup lang="ts">
import { SConfigProvider } from '@vean/ui';

const layout = reactive({ sidebarCollapsed: false });
</script>

<template>
  <SConfigProvider
    persist-theme
    :get-extra-theme="() => ({ sidebarCollapsed: layout.sidebarCollapsed })"
    :on-extra-theme-change="extra => (layout.sidebarCollapsed = Boolean(extra.sidebarCollapsed))"
  >
    <slot />
  </SConfigProvider>
</template>

getExtraTheme runs inside the writer’s reactive sources, so reading a reactive store inside it is enough — a change re-persists with no extra wiring. onExtraThemeChange fires once on mount with the stored payload and again when another tab rewrites the envelope; it does not fire when persistTheme is off. The payload must be JSON-serializable; the theme layer only checks that it is an object, never its content. useTheme().extra exposes the last applied snapshot read-only.

Do not add a second storage key for app config: the provider’s single-writer guarantee only holds while it owns the one key.

In Nuxt the wiring is minimal:

// nuxt.config.ts
export default defineNuxtConfig({
  app: {
    head: {
      // apply the saved theme before first paint
      script: [{ innerHTML: createThemeInitScript(), tagPosition: 'head' }]
    }
  }
});
// app.vue — pass only the environment flag
<script setup lang="ts">
import { SConfigProvider } from '@vean/ui';

const isServer = import.meta.server;
</script>

<template>
  <SConfigProvider :is-server="isServer" persist-theme>
    <slot />
  </SConfigProvider>
</template>

SConfigProvider reads the persisted theme from localStorage on the client; on the server it starts from the default and the inline script corrects it before first paint. Theme state (base / primary / radius / size / mode) and custom presets are managed inside the provider and exposed to descendants via useTheme() from @vean/ui — no prop drilling, no app-level store.

FAQ

Where should I place SConfigProvider? Wrap your application root once, typically in App.vue or the root layout. It must be an ancestor of every component that relies on theme, locale, direction, toast, dialog, or progress context.

How do dir and locale interact?dir takes precedence when provided explicitly. When omitted, ConfigProvider derives direction from locale: registered locales use their declared dir; unregistered locales fall back to a built-in RTL prefix map (e.g. ar → rtl, en → ltr). If locale is also unknown, the final fallback is ltr.

How do I add a locale that is not pre-registered? Import the locale file and register it once: registerLocale(ar) (full registry form) or registerLocale('custom', messages) (shorthand form). Then pass locale="ar" (or your custom key) to SConfigProvider. See the “Loading another supported locale” section above.

How does dark mode work? The engine always generates both light and dark CSS variable sets. The theme.darkSelector option controls how the dark set is scoped: 'class' (default) emits the dark variables under a .dark selector, 'media' emits them under @media (prefers-color-scheme: dark), and any custom string is used verbatim as the selector. With the default 'class' selector, toggle a .dark class on <html> (or any ancestor) to switch to dark mode; with 'media', the theme follows the user’s OS preference automatically.

Can I nest SConfigProvider instances? Yes. Nesting is supported — the inner provider’s context overrides the outer for its subtree. This is useful for embedding an RTL section inside an LTR app, or a differently-themed micro-frontend.

How do I render my own toast UI? Pass customToast to opt out of the default ToastProvider: <SConfigProvider customToast>. Then import SToastProvider (or the Aria ToastProvider) yourself and render custom toast content. The toast() imperative API still works because the Aria toast state is independent of the rendered UI.

Changelog

Version history is grouped by release so you can quickly see what changed for this component across published versions.

v0.51.3

2026-10-27

Compare release

Feature

persist app config in the theme envelope

Scope: config-providerAuthors: soybeanjsView commite1a7d

v0.51.0

2026-10-19

Compare release

Breaking

remove the deprecated top-level size prop

Scope: config-providerAuthors: soybeanjsView commit74615

Refactor

extract pure theme helpers and split the theme runtime hooks

Scope: config-providerAuthors: soybeanjsView commit1fbc1

v0.30.0

2026-08-20

Compare release

Fix

fix toggle dark mode

Scope: config-providerAuthors: soybeanfeView commit8c608

Fix

fix toggle dark mode

Scope: config-providerAuthors: soybeanfeView commit8c608

v0.29.3

2026-06-22

Compare release

Fix

integrate useStyleTag for dynamic CSS handling

Scope: config-providerAuthors: soybeanfeView commit600c3

v0.29.0

2026-06-18

Compare release

Feature

import updateUiAttribute from shadcn-theme

Scope: config-providerAuthors: soybeanjsView commit6679f

Fix

clean up imports and remove unused default preset options check

Scope: config-providerAuthors: soybeanfeView commit2b18c

Refactor

simplify theme handling and improve CSS generation

Scope: config-providerAuthors: soybeanjsView commit11aff

Feature

import updateUiAttribute from shadcn-theme

Scope: config-providerAuthors: soybeanjsView commit6679f

Refactor

simplify theme handling and improve CSS generation

Scope: config-providerAuthors: soybeanjsView commit11aff

Fix

clean up imports and remove unused default preset options check

Scope: config-providerAuthors: soybeanfeView commit2b18c

v0.27.1

2026-05-28

Compare release

Fix

simplify theme watch logic and ensure immediate execution

Scope: config-providerAuthors: soybeanfeView commit8ac58

v0.26.0

2026-05-27

Compare release

Feature

integrate Nuxt detection and remove nuxt prop

Scope: config-providerAuthors: soybeanfeView commit1cd2f

v0.20.0

2026-05-12

Compare release

Optimization

remove unused text direction handling

Scope: config-providerAuthors: soybeanfeView commit8f21f

v0.17.0

2026-05-03

Compare release

Fix

remove unused useStyleTag and integrate cssVars in Primitive

Scope: config-providerAuthors: soybeanjsView commit3996e

v0.6.6

2026-02-09

Compare release

Feature

enhance theme variable management with useStorage

Scope: config-providerAuthors: soybeanjsView commit1a002

v0.0.17

2026-01-19

Compare release

Optimization

cache theme CSS variables

Scope: config-providerAuthors: soybeanjsView commit78e49

Refactor

add theme styling support with Shadcn theme integration

Scope: config-providerAuthors: soybeanjsView commitcb4dd

Refactor

remove unused useStyleTag and bind cssVars to Primitive

Scope: config-providerAuthors: soybeanjsView commita3ee0

Refactor

remove unused theme options from props

Scope: config-providerAuthors: soybeanjsView commitb731c

v0.0.2

2025-11-18

Compare release

Feature

introduce ConfigProvider component with context and types for global configuration management

Scope: config-providerAuthors: soybeanjsView commit1e50e

Feature

add nuxt prop to ConfigProvider for Nuxt application support

Scope: config-providerAuthors: soybeanjsView commit7da82

Feature

wrap main components in ConfigProvider for consistent theming and configuration

Scope: config-providerAuthors: soybeanjsView commitf466e

Feature

implement ConfigProvider component with context and type definitions for enhanced configuration management

Scope: config-providerAuthors: soybeanjsView commit8f219

Feature

enhance theme and size management with dynamic class updates and new utility functions

Scope: config-providerAuthors: soybeanjsView commita1023

Refactor

rename ConfigProvider to SConfigProvider, omit 'iconify' prop, and enhance default props handling

Scope: config-providerAuthors: soybeanjsView commit380f0

Feature

introduce ConfigProvider component with context and types for global configuration management

Scope: config-providerAuthors: soybeanjsView commit1e50e

Feature

add nuxt prop to ConfigProvider for Nuxt application support

Scope: config-providerAuthors: soybeanjsView commit7da82

Feature

wrap main components in ConfigProvider for consistent theming and configuration

Scope: config-providerAuthors: soybeanjsView commitf466e

Feature

implement ConfigProvider component with context and type definitions for enhanced configuration management

Scope: config-providerAuthors: soybeanjsView commit8f219

Feature

enhance theme and size management with dynamic class updates and new utility functions

Scope: config-providerAuthors: soybeanjsView commita1023

Refactor

rename ConfigProvider to SConfigProvider, omit 'iconify' prop, and enhance default props handling

Scope: config-providerAuthors: soybeanjsView commit380f0