Migrating to v9
View SourceThis guide covers upgrading from Phoenix Duskmoon UI v6/v7/v8 to v9. The v9 release is a major rewrite — components now render as HTML Custom Elements and the CSS system has been replaced entirely.
Overview of Breaking Changes
| Area | v6/v7/v8 | v9 |
|---|---|---|
| CSS framework | DaisyUI | @duskmoon-dev/core |
| Tailwind version | v3 | v4+ |
| CSS plugin directive | @plugin "daisyui" | @plugin "@duskmoon-dev/core/plugin" |
| Rendering | Plain HTML | HTML Custom Elements (<el-dm-*>) |
| Module paths | PhoenixDuskmoon.Component.Button | PhoenixDuskmoon.Component.Action.Button |
| Package manager | npm | Bun |
| Elixir version | ~> 1.12 | ~> 1.15 |
Step-by-Step Migration
1. Update Elixir dependency
# mix.exs — before
{:phoenix_duskmoon, "~> 7.0"}
# mix.exs — after
{:phoenix_duskmoon, "~> 9.0"}mix deps.get
2. Replace npm packages with bun
Remove old npm dependencies and install the new ones:
# Remove old packages
bun remove daisyui
# Install new packages
bun add @duskmoon-dev/core @duskmoon-dev/elements
@duskmoon-dev/core replaces both DaisyUI and the old phoenix_duskmoon/theme
phoenix_duskmoon/componentsCSS exports.@duskmoon-dev/elementsprovides the custom element packages that components render as.
3. Update CSS imports
This is the most significant change. The old CSS import pattern using DaisyUI and phoenix_duskmoon exports is replaced entirely.
/* ❌ v6/v7/v8 — REMOVE these */
@plugin "daisyui";
@import "phoenix_duskmoon/theme";
@import "phoenix_duskmoon/components";/* ✅ v9 — ADD these instead */
@import "tailwindcss";
@plugin "@duskmoon-dev/core/plugin";The @duskmoon-dev/core/plugin directive registers the full design system with
Tailwind v4: theme variables, component classes, and design tokens. The old
phoenix_duskmoon/theme and phoenix_duskmoon/components CSS export paths no
longer exist.
Optional: Import built-in themes for light/dark mode support:
@import "tailwindcss";
@plugin "@duskmoon-dev/core/plugin";
@import "@duskmoon-dev/core/themes/sunshine";
@import "@duskmoon-dev/core/themes/moonlight";4. Register custom elements in JavaScript
v9 components render as <el-dm-*> custom elements. These must be registered
in your app.js:
// Register all elements at once
import "@duskmoon-dev/elements/register";
// Or register individually (smaller bundle)
import "@duskmoon-dev/el-button/register";
import "@duskmoon-dev/el-card/register";
import "@duskmoon-dev/el-input/register";
// ... add the elements you useWithout registration, components will render empty — the custom element's
<template> content is inert until the element is defined.
5. Set up LiveView hooks
v9 introduces JavaScript hooks for client-side interactions. Add them to your LiveSocket:
import * as DuskmoonHooks from "phoenix_duskmoon/hooks";
let liveSocket = new LiveSocket("/live", Socket, {
params: { _csrf_token: csrfToken },
hooks: DuskmoonHooks,
});If you have existing hooks, merge them:
import * as DuskmoonHooks from "phoenix_duskmoon/hooks";
import MyHooks from "./my_hooks";
let hooks = { ...DuskmoonHooks, ...MyHooks };See the Hooks guide for details on each hook.
6. Update module imports (if importing individual modules)
Component modules have been reorganized into 8 categories. If you import individual modules directly, update the paths:
# ❌ v6/v7/v8 paths (no longer exist)
alias PhoenixDuskmoon.Component.Button
alias PhoenixDuskmoon.Component.Form.Input
# ✅ v9 paths
alias PhoenixDuskmoon.Component.Action.Button
alias PhoenixDuskmoon.Component.DataEntry.InputIf you use use PhoenixDuskmoon.Component in your view helpers (recommended),
no changes are needed — all components are still imported with the same dm_
prefixed function names.
7. Update CSS class overrides
If you target DaisyUI class names in custom CSS, update them to the BEM naming convention:
/* ❌ v6/v7/v8 DaisyUI classes */
.btn-primary { ... }
.card-body { ... }
.form-control { ... }
/* ✅ v9 BEM classes */
.dm-btn--primary { ... }
.dm-card__body { ... }
.dm-input { ... }The naming convention is:
- Block:
dm-component(e.g.,dm-btn,dm-card) - Modifier:
dm-component--variant(e.g.,dm-btn--primary) - Element:
dm-component__element(e.g.,dm-card__header)
Verifying the Migration
After completing all steps:
- Run
mix compile --warnings-as-errorsto catch any removed module references - Start the dev server with
mix phx.serverand check browser console for:- Missing custom element warnings (add missing
import ... /registerlines) - CSS errors (verify
@plugindirective is correct)
- Missing custom element warnings (add missing
- Verify theme switching works if you use
<.dm_theme_switcher />
Common Issues
Components render empty / invisible
Custom elements are not registered. Add the appropriate import "@duskmoon-dev/el-*/register" line to your app.js.
Styles look wrong or missing
CSS imports still reference DaisyUI or old phoenix_duskmoon exports. Replace with @plugin "@duskmoon-dev/core/plugin".
"module not found" compilation errors
Module paths changed. Use use PhoenixDuskmoon.Component (auto-imports everything) or update individual aliases to the new category-based paths.