Skip to content

Adopting in an existing app

DynUI is easiest to adopt as a narrow personalization layer on one existing surface. Do not start by rebuilding navigation, global layout, or every screen in the product. Start with a surface where structural personalization already hurts: too many modules, competing priorities above the fold, several user modes, or a large rules table that keeps growing.

DynUI is a self-hosted framework. It does not host a control plane, model, component registry, experiment service, analytics warehouse, or team management console.

Those are integration boundaries:

  • your app owns the renderer registry;
  • your profile service owns user identity and the mapping to anonymous ids;
  • your consent system owns the source of truth for consent;
  • your experiment platform may continue to own assignment;
  • your telemetry or warehouse remains the destination for events;
  • your model provider is optional and runs behind your timeout and caching policy.

The framework provides contracts, validation, deterministic generation, privacy helpers, Figma extraction, experiment primitives, and reference adapters.

Choose a surface with these properties:

  • It is important enough that personalization could move a product metric.
  • It is bounded enough to register 5-15 components.
  • It already has real components and data bindings.
  • It has a neutral default that works for new or no-consent users.
  • It can tolerate a product-owned empty state if required data is unavailable.

Good first candidates are dashboards, activity detail pages, home feeds, insight panels, marketplace detail views, or workflow summaries. Avoid checkout, legal, account recovery, or other high-risk surfaces until the team has operated DynUI on a lower-risk path.

Inventory the components that may appear on the target surface. For each one, write down:

  • the component id and version;
  • allowed surfaces;
  • audience rules;
  • variants;
  • required and optional data;
  • slots, if the component can contain other components;
  • accessibility expectations;
  • experiment gates;
  • priority and placement constraints.

The output of this phase is a ComponentManifest. It can be authored by hand or exported from Figma. Either way, it should be schema-valid, lint-clean, and reviewed by design and engineering before it reaches the app.

Map every manifest component id to a real component in your app. Keep a RendererSpec beside the registry so CI can prove the manifest and renderer still agree.

The renderer path should:

  • reject trees that did not pass validateRenderableTree;
  • resolve data bindings in one place;
  • preserve nested slots recursively;
  • isolate component failures with an error boundary;
  • record exposure only for components that actually rendered;
  • show an app-owned fallback state for explicit unrenderable results.

See Renderer implementation for the recommended contract.

Start with deterministic generation. Your profile adapter should map the real user id to an anonymous SignalProfile and include consent flags on every request.

Before launch, verify these cases:

  • no known consent produces a neutral screen;
  • personalization:false withholds archetype and behavior targeting;
  • analytics:false drops behavior ingestion and telemetry capture;
  • a low-confidence or cold-start profile falls back to neutral or conservative components;
  • logs and cache keys do not include real user ids, emails, names, or raw behavior.

Do not add a live model provider until this path is boring.

Phase 4: Run deterministic mode in production

Section titled “Phase 4: Run deterministic mode in production”

Use HeuristicModelProvider for the first rollout. This validates your manifest, renderer, profile adapter, consent handling, telemetry, and operational path without adding model latency or provider failures.

Ship behind your existing release mechanism and watch:

  • validation failures by reason;
  • unrenderable rates;
  • renderer compatibility failures;
  • component render errors;
  • exposure counts by component and variant;
  • no-consent and neutral-screen rates;
  • latency from profile resolution through render.

If the deterministic path is not stable, a model provider will not make it safer.

Treat registered components and variants as the experimental unit. Assignment can come from DynUI’s experiment engine or from your existing platform through an adapter.

Before promoting a component or variant, require:

  • clean assignment and exposure logging;
  • sufficient sample size and runtime;
  • no sample-ratio mismatch;
  • healthy guardrail metrics;
  • a design review of the observed personalized states.

Phase 6: Optional model-assisted composition

Section titled “Phase 6: Optional model-assisted composition”

Use a model only where composition is the point: nested groupings, hierarchy, layout tradeoffs, or interactions that would otherwise become a large rules table. Keep request-time rendering on deterministic or cached trees.

Run live generation in background, cache-warming, or session-boundary flows with:

  • a timeoutMs budget;
  • limited repair attempts;
  • redacted provider errors;
  • deterministic fallback enabled;
  • cache invalidation on manifest, renderer, profile segment, experiment, and relevant context changes.

The model should raise the ceiling. It should not become the floor.

  • One surface selected and scoped.
  • Manifest is schema-valid, lint-clean, and reviewed.
  • Renderer compatibility passes in CI.
  • No-consent and cold-start states are verified.
  • Deterministic generation is the request-time path.
  • unrenderable and validation failures are observable.
  • Exposure is logged only after render.
  • Manifest diffs are reviewed before release.
  • Rollback path restores the previous manifest or disables the DynUI surface.