BitaKit
Learn step by step

5. Add a preference

Declare one saved value, then show its generated Settings page.

Continue with the Counter plugin. Unlike the counter's in-memory store, a setting is a preference saved through BitaKit's settings storage. Browser storage is the default. Here the preference controls how much each click adds, and BitaKit generates its Settings field.

Read more about Settings →

1. Declare one setting

Create src/plugins/counter/settings.ts:

import { defineSettings, setting } from '@bitakit/core';

export default defineSettings({
  step: setting({ default: 1 }),
});

The key is counter.step. You do not need a custom control or page for a number.

2. Read the setting

A component can read it with the existing hook:

// src/plugins/counter/ui/step-label.tsx
'use client';
import { useSetting } from '@bitakit/ui';

export function StepLabel() {
  const step = useSetting('counter.step');
  return <p>Increment by {step.value}</p>;
}

Import StepLabel into the Counter page and render <StepLabel /> where you want the label. The hook reads the same confirmed value as services and Actions.

3. Show the Settings page

If your app was created with Preferences or Shell, this route is already connected. In the minimal learning app, add it once:

  1. Install @bitakit/app-preferences from the same package set as Core. For the source checkout, use the local archive command. With matching published packages, run npm install @bitakit/app-preferences@0.2.
  2. Create src/app/settings/[[...section]]/page.tsx:
'use client';
import { SettingsRoute } from '@bitakit/app-preferences';

export default function Page() {
  return <SettingsRoute />;
}

Run npm run typecheck, restart the dev server after adding the dependency, then open /settings/counter. Change Step to 2. The label on /counter follows the saved value. Reloading the page retains that preference in this browser.

The declaration is the registration:

settings.ts → catalog page → navigation Action + page body

App Preferences creates the navigation entry and places the body in its slot. You do not register the setting again, add a forwarding Action, or attach a second page body.

Select a part of the Settings page

Settings navigation

Setting field

This field comes from step in settings.ts. Core owns its value; App Preferences supplies the standard control.

counter/settings.ts
import { defineSettings, setting } from '@bitakit/core';export default defineSettings({  step: setting({ default: 1 }),});
Select any labeled region to see its source and extension contract. Dashed outlines identify slot outlets. This is a documentation model, not a running settings editor.

4. Use it in the operation

First let the service's existing method accept a number. In services/value.ts, replace just increment:

increment: (step = 1) => state.set(s => ({ count: s.count + step })),

Then replace the Action's execute property, keeping its other properties:

execute: ({ services, settings }) =>
  services.counter.value.increment(settings.counter.step.get()),

With Step set to 2, each click now increases the count by 2. These two fragments are edits to your existing files, not additional files.

One declaration, one value owner

One declaration, one value owner. The declaration supplies defaults and catalog metadata. App Preferences derives navigation and fields. Changes pass through the runtime and settings.storage; server storage applies its own trusted validation and access rules.
The declaration supplies defaults and catalog metadata. App Preferences derives navigation and fields. Changes pass through the runtime and settings.storage; server storage applies its own trusted validation and access rules.

5. Customize only when needed

The generated page is enough for most settings. Select Settings page body in the example above to see ui/settings/page.tsx: it replaces the body while retaining the automatic URL and navigation. SettingField keeps validation, pending state and persistence.

For a page that retains all declared fields, run npx bitakit add settings-page counter instead of writing that file manually; the command will not overwrite an existing file. Use Settings page header slot, After Settings page body or Settings page footer slot to extend the generated page without replacing its fields.

You have a complete feature

The Counter now has a page, state, an operation and a saved preference. The Advanced lessons let other plugins react to it and extend it without editing it.

What you have now

  • This step gave you a saved preference, counter.step, with its default and storage; with App Preferences installed, also its field on a generated Settings page.
  • Others extend it by reading settings.counter.step in their services and Actions, or useSetting('counter.step') in React.

Next: Announce what happened →

Need nextRead
Add content beneath an existing Settings pagePage extensions
Add a toolbar Action or a custom navigation linkSettings slot targets
Add another page or an access boundaryCustom pages
Add an app theme or save personal copiesThemes and appearance
Inspect what's runningRuntime management

Core continues to own setting values. Page visibility and UI boundaries do not authorize server storage or protected operations.

On this page