Shadwire

Search the documentation

Search the documentation and component pages by title and by content.

Get started
Introduction
Installation
shadwire.json
Theming
Dark mode
Tools
CLI
Agent skill
Registry
llms.txt
Guides
Composition
Styling
Forms
Icons
Accessibility
Localisation
Blocks
Overview
Components
Overview
Accordion
Alert
Alert Dialog
Aspect Ratio
Avatar
Badge
Breadcrumb
Button
Button Group
Calendar
Card
Carousel
Chart
Checkbox
Collapsible
Combobox
Command
Context Menu
Data Table
Date Picker
Dialog
Drawer
Dropdown Menu
Empty
Field
Hover Card
Icon
Input
Input Group
Input OTP
Item
Kbd
Label
Menubar
Native Select
Navigation Menu
Pagination
Popover
Progress
Radio Group
Resizable
Scroll Area
Select
Separator
Sheet
Sidebar
Skeleton
Slider
Sonner
Spinner
Switch
Table
Tabs
Textarea
Toggle
Toggle Group
Tooltip

CLI

The development tool that copies component source into your app and helps you keep it up to date with the registry.

After init, run every command through bin/shadwire. The binstub loads the CLI from the app's bundle, so everyone on the project uses the same version. bin/shadwire help COMMAND shows the help for a command.

bin/shadwire status --json        # the project's context
bin/shadwire search <term>       # find a component
bin/shadwire info <name> --json   # its full API
bin/shadwire add <name> --yes     # install it, dependencies included
bin/shadwire list                 # the whole catalog
bin/shadwire diff [name]          # local drift
bin/shadwire diff --exit-code     # non-zero if there is drift (CI)
bin/shadwire update [name] --yes  # reapply the registry (overwrites!)

Commands

Command What it does
shadwire init Prepares the app: writes shadwire.json, installs the shared base files (ui_component.rb, shadwire.css) and the base gems, adds shadwire to the development group, creates the bin/shadwire binstub, and inserts the Tailwind @import.
bin/shadwire add NAME... Installs one or more components along with their registry dependencies, applies gems and importmap pins, and records all of it in shadwire.json.
bin/shadwire list Lists every component in the registry's catalog.
bin/shadwire search QUERY Searches the catalog by name, title, description and when-to-use text, so you can search for things like form, modal or loading.
bin/shadwire info NAME Shows the component's full API: helpers, variants, sizes, props, files, gems, pins and registry dependencies.
bin/shadwire diff [NAME...] Compares the installed files against the registry and reports unchanged, modified or missing, with a unified diff for the modified ones. Read-only.
bin/shadwire update [NAME...] Reapplies the registry's version of the installed components. Overwrites local edits, so run diff first.
bin/shadwire remove NAME... Uninstalls components. It only deletes their own files, never the shared base files or a file another installed component still uses.
bin/shadwire status Describes the whole app in one call: detected stack, installed components with the helpers they define, and drift.
bin/shadwire version Prints the installed version of the CLI.

Reference

status

Describes the project in a single call. Run it first. It is also the only command an agent needs to have in its context.

bin/shadwire status --json
{
  "rails": true,
  "configPresent": true,
  "registryVersion": "0.2.0",
  "stack": { "importmap": true, "stimulus": true, "tailwindcssRails": true },
  "gems": { "view_component": true, "lucide-rails": true },
  "cli": { "gem": true, "binstub": true },
  "tailwind": { "css": "app/assets/tailwind/application.css", "importPresent": true },
Field How to read it
installed[].helpers The ui_* methods defined in this app. Calling the helper of a component that is not installed raises NoMethodError.
installed[].drift unchanged, modified, missing or unknown.
stack.importmap Whether importmap and Stimulus are present. The interactive components need both.
cli.binstub Whether bin/shadwire exists. If it is false, run init.
helpers.includeAllHelpers False means the app turned off Rails' automatic helper inclusion, so each controller needs a helper call for the Ui::*Helper modules.
helpers.legacyHelperPresent An app/helpers/ui_helper.rb left over from before helpers were split per component. It defines helpers for components you have not installed, and you can delete it.

Search looks at the name, title, description and the when-to-use text, so you can search for what you need: form, modal, overlay, loading, right-click.

bin/shadwire search form
bin/shadwire search modal --json

info

The component's full API. Check it before writing ERB so you are not guessing at arguments. It returns whenToUse, usage snippets, requiresStimulus, registryDependencies, the install files, and api.components[] with each class's helper, variants, sizes, props and attributes.

bin/shadwire info dialog --json

helper is null for anything without a ui_* wrapper: the base superclass, the internal parts a parent component renders by itself, and blocks. File contents are left out.

add

bin/shadwire add button dialog --yes

Without --yes, files that already match the registry are skipped, and it asks about the ones you changed. In a non-interactive shell without --yes, everything is skipped and nothing gets installed.

diff

Changes nothing. Marks each file as unchanged, modified or missing, with a unified diff for the modified ones.

bin/shadwire diff
bin/shadwire diff --json
bin/shadwire diff --exit-code    # non-zero when there is drift (fails CI)

update

bin/shadwire diff button          # look first
bin/shadwire update button --yes  # then overwrite

remove

bin/shadwire remove chart --yes

It only deletes files that belong to the components you remove. The shared base files, and any file another installed component still uses, stay in place. Importmap pins that nothing uses any more are listed but not removed.

Flags

Flag Commands Effect
--cwd DIR all Runs against another application directory instead of the current one.
--registry URL all Reads from another registry. Accepts https:// and file:// URLs; use file:// to test a registry you built locally.
--json all but version Prints JSON instead of human-readable output, for agents and CI.
--yes, -y init, add, update, remove Applies file and dependency changes without asking.
--overwrite add, update Overwrites locally modified files without asking.
--no-deps add, update Skips transitive registry dependencies (on by default).
--force init Rewrites an existing shadwire.json and recreates the binstub.
--exit-code diff Exits non-zero when there is drift, so CI can fail on it.

Errors and exit codes

Exit code 0 means the command worked. If a command could not install a dependency, it says so and exits non-zero, so you can rely on the exit code instead of checking the Gemfile yourself.

$ shadwire init --yes
Initialized shadwire (2 base files).
  create  app/components/ui_component.rb
  FAILED  view_component (bundle add failed)
Failed to install: view_component. Run `bundle add view_component` in the app
and re-run this command.
$ echo $?
1

Each error is a single line on stderr that tells you what to do. These are the messages for a malformed shadwire.json, an unreachable registry, a registry that returns HTML, and a missing argument:

shadwire.json is not valid JSON: expected object key, got 'not' at line 1 column 3
Could not reach the registry at https://…/index.json: getaddrinfo(3): Name or service not known
add requires at least one component name

The exception is status, which never fails: it returns "rails": false, registryError or configError as fields and exits 0. That makes it safe to run anywhere, including when an agent loads it.

In CI

Every command accepts --yes (no prompts), --json (JSON output) and --cwd (run in another directory). This job fails the build when an installed component no longer matches the registry:

- name: Install dependencies (with the development group)
  run: bundle install

# Fails when an installed file has diverged from the registry.
- name: Check for Shadwire drift
  run: bin/shadwire diff --exit-code

The job needs the development group installed; without it, bin/shadwire will not run.