Preferences
Apply user preferences to data attributes so your styles can follow them.
Preferences puts your preferences on data-* attributes and keeps them up to date. Use it around a part of your app, or apply a preference to the whole page.
import { createLocalStorage, createPreference } from "@ui-sdk/core"
import { Button } from "@ui-sdk/solid/button"
import { Preferences, usePreference } from "@ui-sdk/solid/preferences"
import { For } from "solid-js"
import { useLog } from "./log"
const view = createPreference({
values: ["list", "grid"],
defaultValue: "list",
attribute: "data-view",
storage: createLocalStorage("ui-sdk-preferences-view"),
})
const files = [
{ name: "Q3 roadmap.pdf", detail: "2.4 MB" },
{ name: "Launch checklist.md", detail: "12 KB" },
{ name: "Offsite photos", detail: "24 items" },
{ name: "Brand assets.zip", detail: "18 MB" },
]
function Example() {
const log = useLog()
const state = usePreference(view)
const choose = (next: "list" | "grid", event: MouseEvent) => {
view.set(next, { reason: "press", event })
log(`Saved the ${next} view`)
}
return (
<Preferences preferences={[view]}>
<div data-slot="stack">
<div data-slot="controls" role="group" aria-label="View">
<Button
aria-pressed={state().value === "list" ? "true" : "false"}
onClick={(event) => choose("list", event)}
>
List
</Button>
<Button
aria-pressed={state().value === "grid" ? "true" : "false"}
onClick={(event) => choose("grid", event)}
>
Grid
</Button>
</div>
<ul data-slot="files" aria-label="Files">
<For each={files}>
{(file) => (
<li>
<span>{file.name}</span>
<span data-slot="hint">{file.detail}</span>
</li>
)}
</For>
</ul>
</div>
</Preferences>
)
}
export { Example }Installation
npm install @ui-sdk/solid @ui-sdk/coreUsage
Define the preference you want to apply. Here, view chooses how a file list is laid out:
import { createPreference } from "@ui-sdk/core"
const view = createPreference({
values: ["list", "grid"],
defaultValue: "list",
attribute: "data-view",
})
export { view }Pass it to Preferences, then style the content by its attribute:
import { Preferences } from "@ui-sdk/solid/preferences"
import { view } from "./preferences"
function FileList() {
return (
<Preferences preferences={[view]}>
<ul data-slot="files">
<li>Q3 roadmap.pdf</li>
<li>Launch checklist.md</li>
</ul>
</Preferences>
)
}
export { FileList }[data-view="grid"] [data-slot="files"] {
display: grid;
grid-template-columns: repeat(2, 1fr);
}Preferences starts with data-view="list". Calling view.set("grid") updates it, and the list becomes a grid. Preferences renders with display: contents, so it adds no box of its own.
Save the Value
Add storage to save the choice between visits:
import { createLocalStorage, createPreference } from "@ui-sdk/core"
const view = createPreference({
values: ["list", "grid"],
defaultValue: "list",
attribute: "data-view",
storage: createLocalStorage("ui-sdk-preferences-view"),
})
export { view }The preference now saves changes in local storage and restores the saved value when the page reloads. You can also use session storage or cookies with createPreference.
Read the Value
Use usePreference when the value affects what your component renders:
import { usePreference } from "@ui-sdk/solid/preferences"
import { view } from "./preferences"
function ViewSwitch() {
const state = usePreference(view)
return (
<button
aria-pressed={state().value === "grid" ? "true" : "false"}
onClick={() => view.set(state().value === "grid" ? "list" : "grid")}
>
Grid
</button>
)
}
export { ViewSwitch }Pass the same preference instance wherever you need its value. usePreference also works outside a Preferences component.
Apply to the Whole Page
Set global: true when defining the preference:
const view = createPreference({
values: ["list", "grid"],
defaultValue: "list",
attribute: "data-view",
global: true,
storage: createLocalStorage("ui-sdk-preferences-view"),
})Keep passing it to Preferences. Its attribute now goes on <html>, so styles throughout the page can use it. A global preference needs only one mounted Preferences component to apply it.
Multiple Preferences
Pass more than one preference in the list. For example, you can apply view alongside a color scheme:
<Preferences preferences={[view, colorScheme]}>
<App />
</Preferences>Each preference uses its own attribute and global option. Wrappers that share a preference instance follow the same value. For independent choices, use separate preference instances and different storage keys.
Server Rendering
On the server, Preferences includes a script that applies saved values before the page paints. CSS can use those attributes immediately. usePreference starts with the server's value and updates after mounting.
Follow the server rendering setup to place Preferences in your app and keep cookie-backed preferences separate for each request.
If your Content Security Policy requires a nonce, pass the nonce from your response:
<Preferences preferences={[view]} nonce={nonce}>
<App />
</Preferences>API Reference
Preferences
preferencesreadonly PreferenceLike[]- The preferences to show, from
createPreferenceorcreateColorScheme. - Example: Usage
usePreference
usePreference(preference) returns an accessor for the preference's current state. Read it in JSX so the content updates when the preference changes. For a preference created with createPreference, the state is { value }. For a color scheme, it also includes system and resolved.