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.
@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… | Embed | Skip |
|---|---|---|
| Lets users choose which AI model to use | ModelSelector | — |
| Lets users bring their own provider API keys (BYOK) | KeyManager | — |
| Shows cost estimates before running a request | CostEstimator | — |
| Only uses platform keys with no user choice | — | All 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.
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.
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.
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.
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:
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.