Step 6 of 10

Phase 5 — Embed the UI

Time: ~25 minutes
Prerequisites: Phase 4 complete (routes and policies configured, resolve works with route IDs)
You'll need: Your app's frontend codebase, the UI packages installed in Phase 1

This phase puts Restormel's embeddable components into your app so end-users can select models and (optionally) manage their own provider credentials. By the end, your app shows a ModelSelector filtered by your policies and optionally a KeyManager for BYOK, both wired to your backend.

Package availability — The UI packages (@restormel/keys-svelte, @restormel/keys-react, @restormel/keys-elements) may not be published to npm yet and can return 404 from npm view. Before following the install steps below, verify: npm view @restormel/keys-svelte version (and the same for keys-react, keys-elements). If any return 404, use the headless path until they are published: keep @restormel/keys only, implement a server-side allowed-models proxy (e.g. GET /api/allowed-models) backed by Restormel evaluate, and use your own model picker UI (or copy patterns from the repo demos). See docs/reference/npm-packages.md in the repo for verify-before-install and 404 handling.
Step 5.1 — Decide what to embed

Not every app needs every component. Use this decision matrix:

If your app…EmbedSkip
Lets users choose which AI model to useModelSelector
Lets users bring their own provider API keys (BYOK)KeyManager
Shows cost estimates before running a requestCostEstimator
Only uses platform keys with no user choiceAll UI components (server-side resolve is enough)

Most apps that reached this phase want ModelSelector. KeyManager is for apps where users supply their own OpenAI/Anthropic/Google keys. CostEstimator is a nice-to-have.

Step 5.2 — Embed ModelSelector (Next.js / React)

Use KeysProvider from @restormel/keys-react and a client component that renders ModelSelector with keys, providers, and onSelect. Wire onSelect to your backend — for example persist to POST /api/preferences with modelId and providerId, or use request-scoped model selection (pass selection per request); both are valid.

Tip — Use next/dynamic with ssr: false to lazy-load the model selector so it doesn't increase your initial page bundle.

You'll see

A model selection UI grouped by provider. Each model shows its availability based on whether a key exists for that provider. Users click a model to select it.

How to test

Start your dev server, navigate to your settings page, confirm the ModelSelector renders with provider groups and models, and click a model to confirm the onSelect callback fires (check the network tab for the preferences API call).

Step 5.3 — Embed ModelSelector (SvelteKit)

Import ModelSelector from @restormel/keys-svelte and createKeys plus provider definitions from @restormel/keys. Create the keys instance and pass keys, providers, and onSelect to the component. Wire onSelect to your backend (e.g. persist via POST /api/preferences or use request-scoped selection — both are valid).

You'll see

The same model selection UI as the React version, rendered natively in Svelte.

How to test

Same as Step 5.2: navigate to the settings page, confirm rendering, click a model, verify the callback.

Step 5.4 — Embed ModelSelector (Web Components / vanilla)

For frameworks not covered by the React or Svelte wrappers, use the Web Components: import @restormel/keys-elements, create the keys instance, set el.keys and el.providers on the <rk-model-selector> element, and listen for the rk-model-selected event to save the selection to your backend.

Pitfall — Web Components require setting object props (keys, providers) via JavaScript properties, not HTML attributes. See Framework compatibility for the full list.

You'll see

The model selector rendered inside a shadow DOM. Theming applies via --rk-* CSS custom properties.

How to test

Open your page in a browser, confirm the custom element renders, click a model, and confirm the rk-model-selected event fires.

Step 5.5 — Filter the model list by policies

The ModelSelector shows all models from the configured providers by default. If you have policies (Phase 4) that restrict which models are allowed, filter the model list so users only see valid choices.

Server-side filtering (recommended): Add an API route (e.g. GET /api/allowed-models) that calls the Restormel evaluate endpoint for each candidate model, using your project Gateway Key from server-side environment variables. Return only the allowed model IDs to the client and configure the component with that list.

Security — Never call the policies API from the browser. Keep RESTORMEL_GATEWAY_KEY server-side only. Use a server proxy like /api/allowed-models and return only the filtered model IDs to the client.

Client-side filtering: If you use local resolve (Phase 2, Step 2.6), you can use keys.entitlements.getAvailableModels(allModelIds) to filter.

You'll see

The ModelSelector shows only models that pass your policies. Blocked models are either hidden or shown as unavailable.

How to test

Add a model_allowlist policy that excludes one model. Refresh the settings page; that model should not appear (or should appear greyed out with "Not allowed").

Step 5.6 — Embed KeyManager (optional — for BYOK apps)

If your app lets end-users bring their own API keys, embed the KeyManager component. It provides a settings panel for users to add, validate, list, and remove their provider credentials. KeyManager sits on top of your own storage and validation endpoints — you implement and own the key storage API (e.g. POST /api/keys, DELETE /api/keys/:id) and any server-side validation; KeyManager is the UI layer that calls them via onKeyAdded and onKeyRemoved.

Security — Treat provider credentials as builder-managed. Raw key material should never be logged or persisted in plaintext. Restormel does not need to custody raw provider secrets by default: store credentials in your own backend/secret store (or use a gateway-backed scheme) and return only masked identifiers and metadata to the UI.

You'll see

A settings panel with an empty state, a form to select a provider and add a credential, feedback, a list of saved credentials (masked), and remove buttons.

How to test

Navigate to settings, add a key (test/invalid is fine to confirm validation), confirm onKeyAdded fires and your API receives the request. With a valid key, confirm validation passes. Delete the key and confirm onKeyRemoved fires.

Step 5.7 — Theme the components

Restormel UI components use --rk-* CSS custom properties for theming. Override them to match your app:

css
rk-model-selector,
.rk-model-selector {
  --rk-bg: #1a1a1e;
  --rk-text: #e8e8ec;
  --rk-accent: #3b82f6;
  --rk-border: #2a2a2e;
  --rk-error: #ef4444;
  --rk-success: #22c55e;
}

The components ship with dark (`.rk-dark`) and light (`.rk-light`) presets.

You'll see

Components render with your app's colour scheme.

How to test

Change a token (e.g. --rk-accent) to something distinct; confirm the accent colour updates on buttons and highlights.

Checkpoint checklist: mark each step complete as you finish it.

Checklist

Prompts for this phase

These are optional and collapsed by default. Use them if you're implementing Phase 5 with a coding agent.

Checkpoint

You now have: ModelSelector embedded and filtered by your policies; (optional) KeyManager for BYOK with callbacks wired to your backend; components themed via --rk-*; callbacks saving user selections. The UI components work alongside your resolve integration from Phases 2–4. When a user selects a model, your backend can pass that to resolveProvider({ model: userSelectedModel }) and Restormel evaluates it against routes and policies.