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
vanilla | fortal | heroui | |
|---|---|---|---|
| Design language | Neutral and minimal, inspired by shadcn/ui | Independent, Fortaleza-inspired, with flat controls and solid panels | HeroUI web 3.2.6 |
| Theme | 32 color tokens, a radius scale, and a type scale | Seven generated palettes, neutral Orla defaults, scaling, radius, and panel modes | HeroUI's 68 color roles, derived like its CSS variables, and a radius scale |
| Component axes | Small default vocabulary | solid, soft, surface, outline, and ghost where applicable; floating toast also has classic | HeroUI's primary, secondary, tertiary, soft, and ghost vocabulary where applicable |
| Best for | A house system you intend to shape | A complete system with contrast-tested colors and consistent focus rings | Starting 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_cliCLI 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 --helpTo 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 vanillaThis writes two files and nothing else:
remix.yaml— your prefix, preset, and UI pathlib/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-runItems: 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.dartFor 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 --diff3. Add
remix add buttonButton 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:
flutter --version --machine— requires Flutter 3.44+flutter pub add— only for requirements you are actually missingflutter pub get, then verify the resolved versions inpubspec.lock- Write the authored files and update the managed barrel
dart formatthe files it wrotedart run build_runner build --build-filter=...— scoped to the generated targets onlydart 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>"| Key | Default | Rules |
|---|---|---|
schema | 3 | The only readable schema. Schemas 1–2 shipped in earlier prereleases and named no registry; such a project is reinitialized, not migrated. |
prefix | Ui | ASCII UpperCamel Dart identifier. Its lowercased form must not be a Dart reserved word. Remix and Mix are reserved for runtime dependencies. |
preset | vanilla | Preset selected across every configured registry. Choose vanilla, fortal, or heroui at init. |
defaultRegistry | @remix | Configured namespace for bare item names. |
registries | official GitHub source | Each namespace records repository, path, requested ref and full commit revision. |
paths.ui | lib/ui | Project-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 editEvery 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 buttonThe 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.dartPlace 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.
| Preset | Theme widget | Theme values and modes |
|---|---|---|
vanilla | AcmeTheme | AcmeThemeData.light(), .dark(), and AcmeThemeMode |
fortal | AcmeTheme | AcmeThemeData.light(), .dark(), and AcmeThemeMode |
heroui | AcmeTheme | AcmeThemeData.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 --diffregistry 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/buttonBare 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 --adoptName 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 --overwriteList 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:
UiThemeDatagained thirteen required colors (card,popover,input,ring, and eightsidebar*colors) and lostfocusRing. A theme you build with the constructor needs the new arguments; start fromUiThemeData.light().copyWith(...)instead.UiTokens.focusRingisUiTokens.ring. Recipes draw the ring at half strength, 3px wide with no offset, and turn a bordered control's outlineringwhile 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, andUiDisclosuredraw their rings withuiFocusRing()throughcontainerEffects(triggerEffectson the accordion and disclosure), so reinstall them with the theme, and replace the helper in your own code.UiTokens.radiusis replaced by four steps derived from the theme's oneradiusvalue (now 10):radiusSm(6),radiusMd(8, the control radius),radiusLg(10), andradiusXl(14, cards).accentis no longer thebordergray: it is the same light gray asmuted, so hover and selected surfaces stay distinct from outlines. A selected toggle no longer draws aprimaryoutline.- There is no pressed step: a press lands on the hover fill.
- In the dark theme, destructive buttons and badges paint
destructiveat 60% rather than solid. UiSidebarLayoutcollapses to 48px (was 72); the dashboard shell keeps its own 72px rail.- Tooltips open as the pointer arrives (no 500ms delay).
UiTabBarandUiTabtake aUiTabsVariant. The defaultfilledis a recessed pill list; passvariant: .lineto both for the underline strip.- The shipped themes set
fontFamilyandmonoFontFamilyto Geist and Geist Mono fromremix_ui_fonts, whichremix add themeadds to yourdependencies. To keep another family, change those two fields intheme_data.dartor pass them tocopyWith. - The root
UiThemenow paints the page background and gives bareTextthe theme's body run and bareIcons itsforeground, so remove anyColoredBoxorDefaultTextStyleyou 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 item | add | add --overwrite |
|---|---|---|
| Not installed | Installs it, reports Added | Installs it, reports Added |
| Fully installed | Successful no-op, reports Preserved | Re-renders, reports Updated |
| Partially installed | Error — refuses to guess | Re-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:
| Package | Required section |
|---|---|
remix, mix_annotations | dependencies |
mix_chart | dependencies when chart is installed |
remix_ui_icons | dependencies when an installed item uses icons (iconLibrary: remix, the default, or rounded) |
remix_ui_fonts | dependencies with the Vanilla preset's theme |
lucide_flutter | dependencies in place of remix_ui_icons when iconLibrary: lucide is selected |
build_runner, mix_generator | either (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.
| Item | Surface you get | Axes |
|---|---|---|
icons | UiIcons | — |
accordion | UiAccordion | — |
app_bar | UiAppBar | — |
avatar | UiAvatar | — |
badge | UiBadge | variant |
button | UiButton | variant, size |
callout | UiCallout | variant |
card | UiCard | — |
chart | UiLineChart, UiBarChart, UiPieChart | — |
checkbox | UiCheckbox, UiCheckboxGroupItem | — |
dashboard_demo | UiDashboardDemo, UiDashboardOverview | — |
dashboard_shell | UiDashboardShell | — |
data_list | UiDataList | — |
data_table | UiDataTable | — |
dialog | UiDialog | — |
disclosure | UiDisclosure | — |
divider | UiDivider | — |
icon_button | UiIconButton | variant, size |
link | UiLink | — |
menu | UiMenu | — |
popover | UiPopover | — |
progress | UiProgress | — |
radio | UiRadio | — |
segmented_control | UiSegmentedControl | — |
select | UiSelect | — |
sidebar | UiSidebar | — |
sidebar_layout | UiSidebarLayout | — |
skeleton | UiSkeleton | — |
slider | UiSlider | — |
spinner | UiSpinner | — |
switch | UiSwitch | — |
tabs | UiTabBar, UiTab, UiTabView | variant |
textfield | UiTextField, UiTextArea | — |
toast | UiToast | variant |
toggle | UiToggle | variant, size |
toggle_group | UiToggleGroup | variant, size |
tooltip | UiTooltip | — |
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 chartIt 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 --overwriteFor HeroUI:
remix add breadcrumbs close_button search_field --diff
remix add breadcrumbs close_button search_field --overwriteIcons 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 reruns64— invalid command or arguments1— 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 composerThese 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.