ShadcnEx
shadcn_ex is a Phoenix component package + tooling pipeline for automated shadcn/ui -> HEEx adaptation.
Goals
- Extract component metadata from upstream shadcn/ui registry sources.
- Auto-detect props, slots, variants, and interactivity signals.
-
Classify components into:
renderable_stateless(safe for generic renderer/codegen)custom_interactive(requires manual adapter/component logic)
-
Promote
use clientwrappers only when schema analysis shows static HTML slot rendering (no hooks, no JSX event handlers, no non-intrinsic slot tags). - Keep generated manifests versioned for CI-driven update bots.
Installation
Add the dependency:
# mix.exs
def deps do
[
{:shadcn_ex, "~> 0.1"}
]
endRun the installer to copy assets and patch your app.js/app.css:
mix shadcn.installThe installer will:
-
Copy
hooks.jsandshadcn_ex.cssto your app'spriv/static/shadcn_ex/ -
Patch
assets/js/app.jswith import statements (managed block) -
Patch
assets/css/app.csswith a Tailwind v4@sourcedirective for class discovery
Each patch is shown as a diff with a y/N prompt before applying.
Installer Options
| Flag | Description |
|---|---|
--force | Skip prompts, auto-apply all patches (CI mode) |
--check | Verify install completeness, exit non-zero on drift |
--no-tailwind-patch |
Skip @source patching in app.css |
--app <name> | Target a specific app in an umbrella project |
For umbrella projects, the installer auto-detects web apps (apps with an assets/ directory). If multiple are found, it prompts which to target. Use --app to skip the prompt.
Verifying Install in CI
mix shadcn.install --checkReturns exit code 0 if all managed blocks and assets are in place, non-zero otherwise.
Consumer Usage
- Optionally validate project compatibility.
use ShadcnExin LiveView/component modules.- Use normal HEEx component syntax.
Optional compatibility check:
mix shadcn.validate_configFor informational-only mode (no failure on warnings):
mix shadcn.validate_config --no-strictIn your module:
defmodule MyAppWeb.PageLive do
use MyAppWeb, :live_view
use ShadcnEx
endIn HEEx — each shadcn sub-component maps to its own function component:
<.button variant="outline" size="sm">Save</.button>
<.card>
<.card_title>Agents</.card_title>
<.card_description>Runtime groups</.card_description>
<.card_content>
<.badge variant="secondary">Configured</.badge>
</.card_content>
<.card_footer>Synced just now</.card_footer>
</.card>JS Hooks
ShadcnEx ships a tiered hook system for components that need client-side interactivity.
Tier 1 — Vanilla JS
Safe hooks using only DOM APIs and optional vanilla JS libraries:
- sidebar — toggle expanded/collapsed state via
data-stateattribute - sonner — toast notifications using CSS classes and design tokens
- embla-carousel — carousel with optional autoplay (requires
embla-carouselnpm package)
Tier 2 — LiveView Bridges
Lightweight DOM utilities that bridge user interactions to LiveView server events:
- command-palette — Cmd+K / Ctrl+K listener, toggles
data-state, pushes LiveView event - drawer — open/close via trigger elements, CSS transitions, Escape to close
Tier 3 — Unsupported
React-only libraries that cannot run without a React runtime. These emit a one-time console warning with guidance:
recharts, react-day-picker, react-hook-form, base-ui, cmdk, vaul
For these components, use a React adapter (e.g., LiveSvelte, LiveReact) or a native Elixir alternative.
Usage
import { initHybridComponents, setupAutoInit } from "shadcn_ex/hooks"
// Initialize all [data-js-mount] elements currently in the DOM
initHybridComponents()
// Or auto-init on LiveView mount events
setupAutoInit()Versioning and Compatibility
mix shadcn.validate_config detects:
-
shadcn style from
config :shadcn_ex, :styleorcomponents.json -
Tailwind major from
assets/package.json(fallback:assets/css/app.css/tailwind config files)
Current compatibility behavior:
new-york-v4requires Tailwind 4new-york/defaultare treated as Tailwind 3- strict mode (default) fails on mismatch or missing required detection signals
Development
These sections are for library maintainers, not consumers.
Extract Manifest
Re-extract component metadata from upstream shadcn/ui:
mix shadcn.extract
mix shadcn.extract --style new-york-v4 --ref mainGenerate Renderer Registry
Rebuild the Elixir registry from the manifest:
mix shadcn.codegenHardcoded Colors Audit
The extraction manifest includes a hardcoded_colors array per component, flagging Tailwind classes that use absolute colors (e.g., bg-white) rather than design tokens. Use this to audit which components may need theme overrides.
Tests
npx tsx --test scripts/extract_shadcn.test.ts
mix test