RemixRemix

Open Code

Install editable Remix component source into your app with the remix CLI

For a guided walkthrough with screenshots and runnable sample projects, start with Build a settings screen. This page is the command and configuration reference.

Open code copies a theme and component recipes into your application. From that moment your team owns those files: no wrapper, no subclass, and no package-level theme override standing between you and the source. Choose the small vanilla preset, the full Fortaleza-inspired fortal preset, or the HeroUI-inspired heroui preset when you initialize the project.

Remix keeps everything that is genuinely hard: rendering, pointer and keyboard handling, focus management, accessibility semantics, and the loading/disabled interaction rules. The installed file only decides what things look like.

remix_cli has its own version and release schedule. The Fortal preset does not add a remix_fortal dependency.

Choose a preset

vanillafortalheroui
Design languageNeutral and minimal, inspired by shadcn/uiIndependent, Fortaleza-inspired, with flat controls and solid panelsHeroUI web 3.2.6
Theme32 color tokens, a radius scale, and a type scaleSeven generated palettes, neutral Orla defaults, scaling, radius, and panel modesHeroUI's 68 color roles, derived like its CSS variables, and a radius scale
Component axesSmall default vocabularysolid, soft, surface, outline, and ghost where applicable; floating toast also has classicHeroUI's primary, secondary, tertiary, soft, and ghost vocabulary where applicable
Best forA house system you intend to shapeA complete system with contrast-tested colors and consistent focus ringsStarting from a HeroUI-like system

Every choice installs local Dart under lib/, uses Remix for behavior, and uses the same add --diff / add --overwrite review workflow. They cannot be mixed inside one initialized source tree because their tokens and variant vocabularies are different.

Install

The registry in this checkout requires Remix ^1.0.0-beta.14, which add declares for you. Beta.14 is not published yet; the currently hosted Remix release is beta.13. Registry pins and their floors remain independent of the CLI version.

Add the CLI as a project-local dev dependency so its version is pinned in your lockfile, not in a machine-global install:

flutter pub add dev:remix_cli

CLI 1.0.0-beta.1 is not published yet. For unreleased ownership or icon-table schema 2 support, use the Git activation instructions below. Exact hosted version instructions will follow publication.

Running the CLI

The command is called remix. How you reach it depends on how it is installed:

# Project-local dev dependency (recommended)
dart run remix_cli:remix <command>

# Global install
dart pub global activate remix_cli
remix <command>

To activate an unreleased revision from Git:

dart pub global activate --source git https://github.com/btwld/remix.git \
  --git-path packages/remix_cli --git-ref main
remix --help

To pin a specific CLI revision, replace main with a full commit SHA. Add Pub's bin directory to PATH if prompted. Git activation isolates the global command from local workspace lockfiles. If a previous --source path activation fails with "cannot resolve to the same set of dependencies", run this Git activation to replace it. You do not need to modify checkout manifests or lockfiles.

Prefer the project-local form to pin the CLI. Registry content is pinned independently to a GitHub commit in remix.yaml. Neither CLI upgrades nor ordinary installs advance that pin.

The examples below are written as remix <command> to match the tool's own usage output. If you are using the project-local form, prefix each one with dart run remix_cli:, for example dart run remix_cli:remix init.

$ remix --help
Install editable Remix UI source.

Usage: remix <command> [arguments]

Global options:
-h, --help       Print this usage information.
    --version    Print the remix_cli version.

Available commands:
  add        Install registry items and their dependencies.
  init       Initialize Remix source installation settings.
  registry   Register and pin GitHub registry sources.

Run "remix help <command>" for more information about a command.

1. Initialize

remix init --preset vanilla

This writes two files and nothing else:

  • remix.yaml — your prefix, preset, and UI path
  • lib/ui/ui.dart — a managed barrel with an export marker block
# remix.yaml
schema: 3
prefix: Ui
preset: vanilla
paths:
  ui: lib/ui
defaultRegistry: "@remix"
registries:
  "@remix":
    repository: btwld/remix
    path: registry
    ref: registry-stable
    revision: "<resolved-full-commit-sha>"

Choose the naming and location up front, because the prefix is baked into every identifier the CLI renders:

remix init --prefix Acme --preset fortal --ui-path lib/design_system

--prefix Acme produces identifiers such as AcmeButton and acmeButtonStyle. The prefix must be an UpperCamel Dart identifier whose lowercased form is not a reserved word, and the UI path must live under lib/. The prefixes Remix and Mix are reserved for runtime dependencies. The preset defaults to vanilla when omitted.

init is also a repair command. Run it again and it recreates whichever of the two files is missing, telling you exactly which one it touched:

Created remix.yaml; preserved lib/ui/ui.dart.

2. Preview before you write

Never let a generator surprise you. Both preview modes run the full preflight (path safety, dependency sections, registry resolution) and then stop before any file is written or any process runs.

remix add button --dry-run
Items: theme -> button
Dependencies: remix@^1.0.0-beta.14, remix_ui_fonts@^0.1.0, mix_annotations@^2.2.0-beta.1, dev:build_runner@^2.10.1, dev:mix_generator@^2.2.0-beta.3
theme: missing; lib/ui/theme/effects.dart, lib/ui/theme/theme_data.dart, lib/ui/theme/theme_scope.dart, lib/ui/theme/tokens.dart
button: missing; lib/ui/components/button.dart
Exports: theme/tokens.dart, theme/theme_data.dart, theme/theme_scope.dart, theme/effects.dart, components/button.dart
Generated: lib/ui/components/button.g.dart

For the actual source, use --diff, which renders the proposed files, formats them, and shows a real git diff against what you have:

remix add button --diff

3. Add

remix add button

Button depends on Theme in the registry, so both are installed, dependencies first. The command then runs a fixed sequence and stops at the first failure:

  1. flutter --version --machine — requires Flutter 3.44+
  2. flutter pub add — only for requirements you are actually missing
  3. flutter pub get, then verify the resolved versions in pubspec.lock
  4. Write the authored files and update the managed barrel
  5. dart format the files it wrote
  6. dart run build_runner build --build-filter=... — scoped to the generated targets only
  7. dart analyze lib/ui
Added theme.
Added button.
Generated lib/ui/components/button.g.dart.

Commit remix.ownership.json with remix.yaml and the installed source. It records the registry owner of authored and generated targets, including missing files; it does not lock their content or hash local edits. All install modes, including previews, enforce these reservations. Older installations need explicit adoption before previewing existing items.

Then use it like any widget:

import 'package:flutter/widgets.dart';

import 'ui/ui.dart';

class SignUpButton extends StatelessWidget {
  const SignUpButton({super.key, required this.onPressed});

  final VoidCallback onPressed;

  @override
  Widget build(BuildContext context) {
    return UiButton.primary(label: 'Create account', onPressed: onPressed);
  }
}

Configuration

remix.yaml contains project settings and pinned named registry sources:

schema: 3
prefix: Ui       # type prefix for every generated identifier
preset: vanilla  # selected across every configured registry
paths:
  ui: lib/ui     # where installed source lives
defaultRegistry: "@remix"
registries:
  "@remix":
    repository: btwld/remix
    path: registry
    ref: registry-stable
    revision: "<resolved-full-commit-sha>"
KeyDefaultRules
schema3The only readable schema. Schemas 1–2 shipped in earlier prereleases and named no registry; such a project is reinitialized, not migrated.
prefixUiASCII UpperCamel Dart identifier. Its lowercased form must not be a Dart reserved word. Remix and Mix are reserved for runtime dependencies.
presetvanillaPreset selected across every configured registry. Choose vanilla, fortal, or heroui at init.
defaultRegistry@remixConfigured namespace for bare item names.
registriesofficial GitHub sourceEach namespace records repository, path, requested ref and full commit revision.
paths.uilib/uiProject-relative, normalized, no traversal, must be under lib/. Symlinks that escape the package root are rejected.

Unknown keys are an error rather than a warning, so a typo fails loudly instead of silently reverting to a default:

$ remix init
FormatException: configuration has unknown keys: style.

The prefix drives two identifier casings at once. With prefix: Acme you get the type AcmeButton and the function acmeButtonStyle.

Choose prefix, preset, and paths.ui before your first add. The CLI will not rename, restyle, or move source it has already installed.

Running init with different values against an existing remix.yaml is a hard error, not a migration.

Editing remix.yaml by hand afterwards conflicts with the prefix, preset, and UI path saved in remix.ownership.json. Every add mode, including --diff, --dry-run, --overwrite, and --adopt, rejects that mismatch. Renaming identifiers or deleting installed files does not update the recorded context or release target reservations.

To recover an accidental change, restore the matching configuration, ownership record, and source from version control. To intentionally choose another prefix, preset, or UI path, initialize a clean installation in a separate app with those settings, install the required items, then reapply your application edits. Do not delete or edit the ownership record to relabel existing source.

For example, an init that conflicts with the existing configuration reports:

FormatException: Existing remix.yaml does not match the requested prefix, preset, and UI path.

What lands in your app

lib/ui/
├── ui.dart                     # managed barrel — CLI owns the marker block
├── theme/
│   ├── tokens.dart             # token and scale surface the recipe reads
│   ├── theme_data.dart         # light() / dark() / copyWith()
│   ├── theme_scope.dart        # inherited theme widget and lookup
│   └── effects.dart            # tints, shadows, focus ring
└── components/
    ├── button.dart             # the recipe — yours to edit
    └── button.g.dart           # generated adapter — do not edit

Every later add drops one more pair into components/ and extends the barrel's marker block. Existing authored source stays untouched. The focused build also includes every installed generated adapter, so a dependency change cannot remove an earlier generated part.

button.dart is a styler function plus two enums:

import 'package:flutter/widgets.dart';
import 'package:mix_annotations/mix_annotations.dart';
import 'package:remix/remix.dart';

enum UiButtonVariant { primary, secondary, outline, ghost, destructive }

enum UiButtonSize { small, medium, large }

@MixWidget(target: RemixButton.new)
ButtonStyler uiButtonStyle({
  UiButtonVariant variant = UiButtonVariant.primary,
  UiButtonSize size = UiButtonSize.medium,
  ButtonStyler style = const ButtonStyler.create(),
}) {
  return _base(size)
      .merge(_variantStyle(variant))
      .onDisabled(
        ButtonStyler().wrap(WidgetModifierConfig.opacity(0.5)),
      )
      .merge(style);
}

ButtonStyler _base(UiButtonSize size) => ButtonStyler().minHeight(
  switch (size) {
    UiButtonSize.small => 32,
    UiButtonSize.medium => 36,
    UiButtonSize.large => 40,
  },
);

ButtonStyler _variantStyle(UiButtonVariant variant) => switch (variant) {
  UiButtonVariant.primary => ButtonStyler().color(const Color(0xFF171717)),
  _ => ButtonStyler(),
};

@MixWidget generates UiButton into button.g.dart, including one named constructor per variant (UiButton.primary, .secondary, .outline, .ghost, .destructive).

The Fortal preset

Initialize Fortal once, then use the same item commands and ownership workflow:

remix init --prefix Acme --preset fortal
remix add button

The application receives AcmeTheme, AcmeTokens, AcmeButton, and acmeButtonStyle; no installed identifier is named Fortal. Button brings its shared base_button recipe, while the theme arrives as a complete local layer:

lib/ui/
├── ui.dart
├── theme/
│   ├── theme.dart
│   ├── fortal_colors.dart
│   ├── palette.dart
│   ├── computed.dart
│   ├── control_styles.dart
│   ├── tokens.dart
│   ├── theme_data.dart
│   ├── theme_scope.dart
│   └── surface_frame.dart
└── components/
    ├── base_button.dart
    ├── button.dart
    └── button.g.dart

Place AcmeTheme below the application host. For a routed WidgetsApp, wrap the child of WidgetsApp.builder with AcmeTheme. Routes and dialogs then inherit its tokens and text defaults. The theme example lives in lib/ui/theme/theme_scope.dart.

These are application files, not forwarding wrappers around a package. The authored recipes and generated OKLCH palettes are copied from the same source checked by Fortal's specification, contrast tests, and owned goldens. remix remains the behavior and styling-engine boundary; mix_annotations, build_runner, and mix_generator support adapter generation. icons additionally installs remix_ui_icons, and chart installs mix_chart. The preset adds no direct mix, naked_ui, or remix_fortal dependency.

The source is deliberately editable. Change the token map, add an accent or variant, or merge a one-instance styler last. Future CLI versions never merge over those decisions automatically: inspect remix add <item> --diff, then use --overwrite only when you want the new authored file wholesale.

Application host and appearance

Open Code documentation and runnable examples use WidgetsApp. For a routed application, install the generated theme widget in WidgetsApp.builder and pass its child through. That child is the Navigator; keeping the theme above it makes the active theme available to routes, dialogs, and overlays. See the complete routed host example.

The generated theme widgets expose theme, darkTheme, and mode. A root without arguments supplies the preset's light and dark defaults and follows the system. A nested theme inherits its parent's configured pair and active selection. It installs its own complete token set rather than merging with the scope above it, so a token override made with MixScope.inherit does not apply below a nested theme; place the override inside the nested scope.

PresetTheme widgetTheme values and modes
vanillaAcmeThemeAcmeThemeData.light(), .dark(), and AcmeThemeMode
fortalAcmeThemeAcmeThemeData.light(), .dark(), and AcmeThemeMode
herouiAcmeThemeAcmeThemeData.light(), .dark(), and AcmeThemeMode

Use your configured prefix in place of Acme. The same parameters apply to every preset:

AcmeTheme(
  theme: const AcmeThemeData.light(),
  darkTheme: const AcmeThemeData.dark(),
  mode: AcmeThemeMode.system,
  child: child,
)

For Fortal, its AcmeTheme and AcmeThemeData constructors also accept accent, gray, radius, scaling, panel background, and background visibility. Set shared design options on both appearances when customizing the pair.

mode accepts system, light, and dark. The root reacts to platform brightness changes in system mode. An explicit mode overrides the system; a nested scope with no mode inherits the parent's active selection, even if a nearer MediaQuery reports a different system brightness. Nested scopes can explicitly select another mode from the inherited pair.

theme is the base and fallback. When you supply a custom theme without a darkTheme, it is used in both modes; colors are not automatically inverted. Supplying only darkTheme retains the inherited or generated light theme and does not force dark mode. Supplying neither retains both preset defaults.

Generated themes take a child. Keep WidgetsApp.builder above the Navigator and use its child; no builder constructor is required. To read the newly installed theme while building, put Flutter's Builder below the theme and use that callback's context.

The downloadable tutorial apps show the same API in the Vanilla and Fortal presets. Their generated theme files are editable application source; regeneration still preserves existing authored files unless you explicitly use --overwrite. The CLI installs the theme layer, not a complete app host or appearance picker. Selection controls and persistence belong to the application. The dashboard is a reference consumer of the same theme behavior.

The theme API has a hard cutoff: theme constructors accept theme, darkTheme, and mode. The default theme's data parameter, FortalTheme's brightness parameter, and the config createScope shortcut have been removed. Use theme for a fixed theme and mode for appearance selection. Direct Fortal design options such as accent still override the supplied theme values.

The three levels of customization

Edit the recipe: change it for the whole app, permanently. It is your file.

Retheme a subtree: one copyWith restyles every button beneath it, because the recipe reads tokens and the theme decides what tokens resolve to:

import 'package:flutter/widgets.dart';

import 'ui/ui.dart';

Widget rounded(Widget child) => UiTheme(
  theme: const UiThemeData.light().copyWith(
    primary: const Color(0xFF4F46E5),
    radius: const Radius.circular(999),
  ),
  child: child,
);

Override one instance: style is merged last, so it beats the resolved recipe without forking it:

import 'package:flutter/widgets.dart';
import 'package:remix/remix.dart';

import 'ui/ui.dart';

Widget publishButton(VoidCallback publish) => UiButton.primary(
  label: 'Just this one',
  style: ButtonStyler()
      .color(const Color(0xFF7C3AED))
      .onHovered(ButtonStyler().color(const Color(0xFF5B21B6))),
  onPressed: publish,
);

State fragments merge by state, not by depth. An override that must beat the recipe's hover fill has to be declared as a hover fragment too; a bare .color(...) only replaces the idle one.

Updating

Registry content releases independently of the CLI. Update a pin explicitly, then review installed source; no automatic merge or installed-source lockfile is used.

remix registry update @remix
remix add button --diff

registry update resolves the recorded ref again; for the official registry that is registry-stable, the newest promoted commit. A SHA or tag pin resolves to the same commit, so move it with --ref <sha>, or return to the branch with --ref registry-stable.

Register additional public GitHub sources with the same preset:

remix registry add @company --repository owner/repo --path registry --ref v1
remix add @company/button

Bare dependencies resolve within their owning registry. Qualified dependencies require explicit registration; they cannot add or redirect sources. Cross-registry cycles, conflicting targets and incompatible package requirements fail before writes.

For projects from earlier prereleases, complete legacy source adoption before using these preview and overwrite commands. Restore a previously committed remix.yaml pin to roll back the source selection; installed files are not reverted.

--diff renders the new template, formats it, and runs a real git diff against what is on disk. If the output is empty you are current:

No authored-source differences.

If you have edited the recipe, the diff never comes back empty: it reports your own edits every time, whether or not the template moved. Read the hunks, not the exit state. To see the template alone, compare against a copy of the file you installed, or run the diff in a scratch project that has never been edited.

CLI updates do not change registry pins. add, --diff, and --dry-run fetch only the configured commits and never update configuration. Remote failures never substitute older or newer content. Private repositories, arbitrary HTTP sources, local registries and persistent offline caching are not supported.

If you like the upstream version, take it wholesale:

remix add button --overwrite

--overwrite replaces only the items you named. Already-installed registry dependencies are untouched, so updating Button never silently rewrites a Theme you have customized:

Preserved theme.
Updated button.

If you only want part of the upstream change, copy it out of the diff by hand. That is the intended workflow. The file is yours.

Legacy source adoption

CLI 0.1.0 and 1.0.0-beta.0 did not record target ownership, even for schema-3 projects. Upgrade the project-local CLI before recovery. Keep an existing schema-3 configuration; for schemas 1 or 2, remove remix.yaml and rerun remix init with the original prefix, preset, and UI path (schema 1's default preset corresponds to vanilla). Reinitialization preserves installed source and generated adapters.

Every add mode rejects existing targets with unknown ownership. After verifying that each item came from the configured registry and preset, preview and adopt it explicitly. For Vanilla Button with an existing Theme:

remix add theme button --adopt --diff
remix add theme button --adopt

Name every existing unknown dependency as well as the item itself; for an existing Fortal Button installation, also name base_button. The preview writes nothing. Adoption records your verified provenance in remix.ownership.json and preserves authored edits, while otherwise running normal installation, dependency resolution, missing adapter generation, and analysis. Commit that record with remix.yaml and the installed source.

--adopt cannot be combined with --overwrite, cannot replace a known different owner, and does not verify provenance for you. If provenance is uncertain or source from different systems was mixed, restore a known matching configuration, ownership record (if one existed), and source, or start a clean installation and reapply your edits. Once adopted, use ordinary --diff and explicit --overwrite for updates. Do not downgrade to a CLI that ignores the ownership record.

Moving an older Vanilla install onto the current theme

Complete legacy source adoption first if your installation has unknown target ownership.

The Vanilla theme was rebuilt on a new token set. Every recipe reads the new tokens, and --overwrite replaces only the items you name, so update the theme together with every Vanilla item you have installed, in one command:

remix add theme button card dialog tabs --diff
remix add theme button card dialog tabs --overwrite

List your own installed items in place of the example ones. An item left out still reads the old token names and stops compiling once the theme changes.

Then adjust any code of your own that read the old names:

  • UiThemeData gained thirteen required colors (card, popover, input, ring, and eight sidebar* colors) and lost focusRing. A theme you build with the constructor needs the new arguments; start from UiThemeData.light().copyWith(...) instead.
  • UiTokens.focusRing is UiTokens.ring. Recipes draw the ring at half strength, 3px wide with no offset, and turn a bordered control's outline ring while it has focus. A toggle-group option draws the band inside itself, because the group clips its options.
  • uiFocusRingDecoration() is gone. UiTab, UiToggle, UiToggleGroup, UiMenu, UiAccordion, and UiDisclosure draw their rings with uiFocusRing() through containerEffects (triggerEffects on the accordion and disclosure), so reinstall them with the theme, and replace the helper in your own code.
  • UiTokens.radius is replaced by four steps derived from the theme's one radius value (now 10): radiusSm (6), radiusMd (8, the control radius), radiusLg (10), and radiusXl (14, cards).
  • accent is no longer the border gray: it is the same light gray as muted, so hover and selected surfaces stay distinct from outlines. A selected toggle no longer draws a primary outline.
  • There is no pressed step: a press lands on the hover fill.
  • In the dark theme, destructive buttons and badges paint destructive at 60% rather than solid.
  • UiSidebarLayout collapses to 48px (was 72); the dashboard shell keeps its own 72px rail.
  • Tooltips open as the pointer arrives (no 500ms delay).
  • UiTabBar and UiTab take a UiTabsVariant. The default filled is a recessed pill list; pass variant: .line to both for the underline strip.
  • The shipped themes set fontFamily and monoFontFamily to Geist and Geist Mono from remix_ui_fonts, which remix add theme adds to your dependencies. To keep another family, change those two fields in theme_data.dart or pass them to copyWith.
  • The root UiTheme now paints the page background and gives bare Text the theme's body run and bare Icons its foreground, so remove any ColoredBox or DefaultTextStyle you added around the scope for that. A nested scope in the other brightness recolors the text and icons below it.

The Remix drift notice

Each registry revision is authored against one remix version, and the constraint it installs is a caret, so flutter pub upgrade can move you onto a later remix that these templates were never tested against. add reports that after it resolves your lockfile:

Resolved remix 1.0.0; this registry revision was authored against 1.0.0-beta.14. Your pin does not move on its own — run remix registry update @remix, then review with --diff.

Upgrading the CLI does not change the pin, so the notice names registry update. When the pin is a SHA or tag rather than registry-stable, it adds --ref <newer ref>, since a bare update would resolve the same commit.

It is a notice, not a failure. The install completes. A remix below the authored version is a different case and still fails outright, because the templates would not compile against it.

The notice needs a resolved lockfile, so it prints only on a real add. --dry-run and --diff stop before flutter pub get and never show it.

Reinstall behavior

State of the itemaddadd --overwrite
Not installedInstalls it, reports AddedInstalls it, reports Added
Fully installedSuccessful no-op, reports PreservedRe-renders, reports Updated
Partially installedError — refuses to guessRe-renders every file, reports Updated

A partial install is an error rather than a silent repair because the CLI cannot know whether the missing file was deleted on purpose. Use --diff to look, then --overwrite deliberately.

Generated adapters are not your problem. button.g.dart is regenerated whenever its authored source is rewritten or the adapter is missing, and the CLI verifies the file exists afterwards rather than assuming the build worked. The current mix_generator writes this. qualifiers that flutter_lints reports as infos, so exclude lib/ui/**/*.g.dart under analyzer: exclude: in analysis_options.yaml.

Removing an item

There is no remove command. Delete the files and the matching export lines from the managed block in lib/ui/ui.dart. The next add will treat the item as missing and reinstall it cleanly.

Dependency rules

The CLI checks your pubspec.yaml but never rewrites a declaration you already made. It adds only what is missing, and preserves hosted, path, Git, custom-hosted, and override declarations byte-for-byte.

Section placement is enforced, because the installed source lives under your lib/ and is compiled into anything that depends on your package:

PackageRequired section
remix, mix_annotationsdependencies
mix_chartdependencies when chart is installed
remix_ui_iconsdependencies when an installed item uses icons (iconLibrary: remix, the default, or rounded)
remix_ui_fontsdependencies with the Vanilla preset's theme
lucide_flutterdependencies in place of remix_ui_icons when iconLibrary: lucide is selected
build_runner, mix_generatoreither (build-time only)

A runtime package declared only under dev_dependencies fails before any process runs or file is written:

$ remix add button
FormatException: remix is declared under dev_dependencies but installed source
imports it at runtime. Move remix to dependencies in pubspec.yaml and rerun.
No source was written.

Declaring the same package in both sections is rejected the same way. Fix your manifest and rerun; the CLI will not edit it for you.

Vanilla preset catalog

The official registry is deliberately small, and every item after theme depends on it:

theme installs tokens.dart, theme_data.dart, theme_scope.dart, and effects.dart, giving you UiTokens (including the fixed spacing, size, stroke, opacity, and motion scales), UiThemeData, UiTheme, and the shared tint, shadow, and focus-ring helpers the recipes draw on. It also adds remix_ui_fonts, the package that bundles Geist and Geist Mono, which the shipped themes set the text steps in. Every component item installs one authored file plus its generated adapter under components/<item>.dart. Eight items differ: icons installs app-owned icons.dart with no generated adapter; chart generates three adapters over mix_chart; data_table also pulls in checkbox, icon_button, and select, because its selection column, pager, and page-size control are those components; adding it installs all four; sidebar pulls in toggle and tooltip, because a destination is a toggle and a collapsed rail labels it with a tooltip. toast pulls in button and icon_button for its action and close control. sidebar_layout is a layout with no Spec or generated adapter; it pulls in sidebar to compose into its row and compact sheet. dashboard_shell composes those existing primitives, with app_bar as its page header (title, an optional Widget? headerTitle to override the default Text(title) in the app bar, and headerActions), into a host-controlled responsive shell (collapsedWidth defaults to 72 vs 48 on standalone UiSidebarLayout). It installs no charts, tables, sample data, routes, or application entry point. dashboard_demo adds the five-destination reference application: Overview, Chat, Customers, Orders, and Settings. Product content and navigation match across presets while rendering each preset's own components and variants. The starter includes deterministic Agent flows, complete customer and order tables, overview charts, appearance and notification chrome, and local toast feedback. Built-in chrome can be disabled; caller actions append to it and caller account content replaces the default. It leaves the application, routes, authentication, persistence, and backend search behavior with the host.

The registry requires Remix beta.11, which includes RemixSidebar and the Toast APIs. The installer rejects older resolved versions before it writes source.

ItemSurface you getAxes
iconsUiIcons—
accordionUiAccordion—
app_barUiAppBar—
avatarUiAvatar—
badgeUiBadgevariant
buttonUiButtonvariant, size
calloutUiCalloutvariant
cardUiCard—
chartUiLineChart, UiBarChart, UiPieChart—
checkboxUiCheckbox, UiCheckboxGroupItem—
dashboard_demoUiDashboardDemo, UiDashboardOverview—
dashboard_shellUiDashboardShell—
data_listUiDataList—
data_tableUiDataTable—
dialogUiDialog—
disclosureUiDisclosure—
dividerUiDivider—
icon_buttonUiIconButtonvariant, size
linkUiLink—
menuUiMenu—
popoverUiPopover—
progressUiProgress—
radioUiRadio—
segmented_controlUiSegmentedControl—
selectUiSelect—
sidebarUiSidebar—
sidebar_layoutUiSidebarLayout—
skeletonUiSkeleton—
sliderUiSlider—
spinnerUiSpinner—
switchUiSwitch—
tabsUiTabBar, UiTab, UiTabViewvariant
textfieldUiTextField, UiTextArea—
toastUiToastvariant
toggleUiTogglevariant, size
toggle_groupUiToggleGroupvariant, size
tooltipUiTooltip—

The fortal catalog contains every item above and adds base_button, code, heading, kbd, text, and typography. base_button and typography are shared authored recipes that arrive transitively with the components that use them. Fortal's component names use the configured prefix, so an Acme installation exposes AcmeSidebar, AcmeHeading, and AcmeText alongside its generated controls.

The heroui catalog follows HeroUI's names. Beside shared names such as button, select, and tabs, it adds alert, avatar_group, breadcrumbs, button_group, checkbox_group, chip, close_button, dropdown, field_layout, input_group, modal, radio_group, search_field, separator, surface, switch_group, table, textarea, toggle_button, and toggle_button_group. It ships no app_bar, sidebar, sidebar_layout, dashboard, chart, or icons items and no Agent items. Families that need behavior Remix does not provide, such as Form and ComboBox, are not included.

One add installs every named item plus shared dependencies once. The names above use the default Ui prefix; --prefix Acme gives you AcmeButton and friends. An axis is an enum the recipe declares, and a recipe whose axis is named variant also gets one named constructor per value, so UiBadge.destructive(label: 'Failing') works out of the box.

A parameter is not an axis. UiDivider takes an orientation, but it is a plain Flutter Axis the recipe accepts rather than an enum of its own, so it generates no named constructors.

Charts in the Vanilla preset

The Vanilla preset's chart is an optional extension with its own compact recipe:

remix add chart

It installs one editable recipe and generates UiLineChart, UiBarChart, and UiPieChart. Line series can draw lines or areas. Bar data can be grouped, stacked, or floating. A positive centerRadius makes a pie chart a donut.

mix_chart retains the hard contract: data validation, rendering, pointer interaction, transitions, tooltips, and chart semantics. Your copied file owns the axes, grid, shared series geometry, and tooltip appearance. Series colors come from the Theme's chart1 to chart5 tokens. A Theme installed by an earlier CLI build lacks them: add the five tokens to tokens.dart and theme_data.dart before adding chart.

Import chart data directly and give the chart finite dimensions:

import 'package:flutter/widgets.dart';
import 'package:mix_chart/mix_chart.dart';

import 'ui/ui.dart';

final chart = SizedBox(
  height: 240,
  child: UiLineChart(
    semanticsLabel: 'Weekly revenue',
    showMarkers: true,
    series: [
      LineSeries(
        id: 'revenue',
        label: 'Revenue',
        points: [
          ChartPoint(id: 'mon', x: 0, y: 18),
          ChartPoint(id: 'tue', x: 1, y: 31),
        ],
      ),
    ],
  ),
);

The palette is UiTokens.chart: chart1 to chart5 in series order. The shipped values are a neutral gray ramp, the same five grays in both brightnesses, so series are told apart by lightness. Pie labels are off by default; a chart that turns them on colors each slice's label with uiPieSliceLabelColor, which picks foreground or background by contrast. Edit the tokens in UiThemeData for a design-system change. Pass palette or style for a one-chart override. The UI barrel does not re-export mix_chart.

In Vanilla and Fortal, remix add icons installs a small app-owned alias surface. Registry templates resolve the iconLibrary in remix.yaml to either RemixIcons (the default), RoundedIcons, or LucideIcons, and add the selected package. Outline and Rounded each draw every icon the presets use, so neither adds Lucide. See Icons in registry presets for how presets size those icons. Four Vanilla and Fortal items use icons: icons, dashboard_shell, dashboard_demo, and support. HeroUI has no icons item; its three icon users are breadcrumbs, close_button, and search_field. To switch an existing install, change iconLibrary, then reinstall every one of those you have installed in one command, reviewing first (--overwrite replaces only the items you name, so one left out keeps the old library):

remix add icons dashboard_shell dashboard_demo support --diff
remix add icons dashboard_shell dashboard_demo support --overwrite

For HeroUI:

remix add breadcrumbs close_button search_field --diff
remix add breadcrumbs close_button search_field --overwrite

Icons you reference in your own code stay as you wrote them. Import package:remix_ui_icons/remix_ui_icons.dart directly when you need the rest of the Vector UI pack: RemixIcons (the Outline family) and RoundedIcons.

Upgrading an app from remix_ui_icons 0.1.x: the registry now requires ^1.0.0-beta.1, which replaces every Radix glyph, so the installer refuses to write source until your pubspec.yaml declares it. Update the constraint, then follow the package's migration guide for your own icon references: 31 renamed names keep working as deprecated aliases until 2.0.0, and the guide names a Lucide icon for each removed one.

Some Remix widgets are behavioral and carry no style, so the registry has nothing to render for them. RemixCheckboxGroup, RemixRadioGroup, RemixTabs, and RemixAccordionGroup are the four in this catalog: import them from package:remix/remix.dart and put the installed adapters inside.

toggle_group, segmented_control, menu, and select work the other way round. Their options are data, not widgets, and the parent's own recipe carries the option style, so one add covers the container and every row, and a row in a loop cannot be left unstyled.

One host note: an installed text field needs an Overlay ancestor once it takes focus, for its selection handles. MaterialApp, CupertinoApp, and any WidgetsApp with routes already provide one; a bare WidgetsApp(builder: ...) does not.

Exit codes

  • 0 — success, including help, version, dry-run, diff, and no-op reruns
  • 64 — invalid command or arguments
  • 1 — configuration, registry, dependency, process, generation, analysis, or filesystem failure

If generation or analysis fails after installation, the authored source stays on disk for inspection. Fix the reported problem and rerun the same command.

Agent surfaces in the Vanilla and Fortal presets

activity, answer, composer, execution, message, permission, plan, and transcript install unstyled, application-owned behavior source in either the Vanilla or Fortal preset. Each <component>_recipe item adds a complete preset-specific recipe bundle. The private authoring source (registry_source) is not a consumer dependency. The shared models files are exported from the local UI barrel; support helpers install transitively without barrel exports. Appearance remains host-owned.

dart run remix_cli:remix add composer

These surfaces use @MixableSpec. The CLI enables the supported Mix generator's opt-in spec-styler builder for their installed source paths in build.yaml, preserving other builder settings and comments. Explicit exclusions or disabled generation are reported before writes. --dry-run and --diff remain read-only. Agent recipes extend the Vanilla and Fortal presets; they do not constitute another preset.

Available styled items: activity_recipe, answer_recipe, composer_recipe, execution_recipe, message_recipe, permission_recipe, plan_recipe, and transcript_recipe. Each installs only its component and styled-control closure.

View page source on GitHub