DaisyUI logo

A Ruby UI component library for DaisyUI using Phlex

Installation

1. Install CSS dependencies

You can install TailwindCSS and DaisyUI either via a JS bundler or via importmaps.

JS Bundler

TailwindCSS

Install TailwindCSS by following the instructions in the TailwindCSS documentation, using either the Tailwind CLI or PostCSS.

DaisyUI

Install DaisyUI by following the instructions in the DaisyUI documentation as a Node package.

Importmaps

TailwindCSS with DaisyUI

You'll need to download a TailwindCSS standalone CLI that comes bundled with DaisyUI by following the instructions in the tailwind-cli-extra repo.

Afterwards, place it somewhere in your project, e.g. in the bin directory.

If you want to compile the standalone TailwindCSS CLI with DaisyUI yourself, you can follow the instructions here.

tailwindcss-rails gem

Install tailwindcss-rails gem for Rails to automatically include your TailwindCSS stylesheets when the asset pipeline compiles your assets.

For this, you'll need to install the gem by following the instructions in the tailwindcss-rails repo.

Finally, you'll need to set the TAILWINDCSS_INSTALL_DIR environment variable in your Rails app pointing to the directory where you placed the binary from the tailwind-cli-extra repo mentioned above. e.g. TAILWINDCSS_INSTALL_DIR=bin

2. Install Ruby dependencies

Install Phlex

Install Phlex by following the instructions in the Phlex documentation.

Install DaisyUI gem

  1. Add the DaisyUI gem to your Gemfile:
bundle add daisyui
  1. (Optional) Include the DaisyUI module in ApplicationComponent:
class ApplicationComponent < Phlex::HTML
  include DaisyUI
end

This will allow you to use DaisyUI components using the short-form syntax. For example:

class SomeView < ApplicationView
  def view_template
    Button :primary do
      "Hello, world!"
    end
  end
end

If you don't include DaisyUI, you can still use the namespaced syntax:

class SomeView < ApplicationView
  def view_template
    render DaisyUI::Button.new(:primary) do
      "Hello, world!"
    end
  end
end

Consider not including DaisyUI in ApplicationComponent if:

  • You have your own component library with the same component names as DaisyUI.
  • You're including your own components module in ApplicationComponent.

In this scenario, including both DaisyUI and your own component library in ApplicationComponent will lead to naming conflicts.

  1. Update your tailwind.config.js file to include DaisyUI component styles:
const execSync = require("child_process").execSync;
const outputDaisyUI = execSync("bundle show daisyui", { encoding: "utf-8" });
const daisyUIPath = outputDaisyUI.trim() + "/**/*.rb";
module.exports = {
  content: [
    // ... other paths
    daisyUIPath,
  ],
};
  1. Update your tailwind.config.js file to detect TailwindCSS classes in Ruby files.
module.exports = {
  content: [
    // ... other paths
    //
    // Note the "rb" extension at the end
    "./app/views/**/*.{erb,haml,html,slim,rb}",
  ],
};

Compatibility Notes

@tailwindcss/forms plugin

If you're using the @tailwindcss/forms plugin alongside DaisyUI, you may encounter styling conflicts with form components like Toggle, Checkbox, and Radio. The forms plugin adds default checkbox/radio styling that can interfere with DaisyUI's custom styling.

Solution: Add the following CSS to your stylesheet to override the forms plugin styling for DaisyUI components:

/* Override @tailwindcss/forms checkbox styles for DaisyUI components */
.toggle,
.checkbox,
.radio {
  background-image: none !important;
}
.toggle:checked,
.checkbox:checked,
.radio:checked {
  background-image: none !important;
}

Alternatively, you can configure @tailwindcss/forms to use the class strategy instead of base, which only applies styles when you explicitly add form classes:

// tailwind.config.js
plugins: [
  require('@tailwindcss/forms')({
    strategy: 'class', // only apply form styles to elements with form-* classes
  }),
],

The :popover Dropdown modifier renders the menu in the browser top layer (so it escapes overflow clipping) and positions it next to the trigger using CSS anchor positioning (anchor-name / position-anchor). This works with zero JavaScript on Chrome/Edge 125+, Safari 26+, and Firefox 147+.

Dropdown(:popover, :end) do |dropdown|
  dropdown.button(:ghost, :sm) { "Actions" }
  dropdown.menu(:sm, class: "w-52") do |menu|
    menu.item { a(href: "#") { "Edit" } }
    menu.item { a(href: "#") { "Delete" } }
  end
end

On older engines (Safari < 26, Firefox < 147) the popover still opens in the top layer, but without CSS anchor positioning it falls back to the viewport default (DaisyUI centers it via @supports not (position-area)), so it is not positioned next to the trigger.

To position correctly on those browsers with no application code, pin the OddBird CSS anchor positioning polyfill and load it lazily:

# config/importmap.rb
pin "@oddbird/css-anchor-positioning", to: "https://ga.jspm.io/npm:@oddbird/css-anchor-positioning@1/dist/css-anchor-positioning.fn.js", preload: false
// app/javascript/application.js — load only when the browser lacks native support
if (!CSS.supports("anchor-name: --x")) {
  import("@oddbird/css-anchor-positioning").then(({ default: polyfill }) => polyfill())
}

Modern browsers fetch nothing extra; the polyfill loads only where it is needed. For WAI-ARIA roving keyboard navigation inside role="menu" menus (which a CSS polyfill cannot provide), see the optional Stimulus controller below.

Optional daisy-dropdown Stimulus controller

The :popover dropdown needs no JavaScript on modern browsers. For two specific cases — a JS positioning fallback on browsers without CSS anchor positioning, and roving keyboard navigation over role="menu" items — the gem ships an opt-in Stimulus controller. It deliberately does not re-implement open/toggle, light-dismiss, or Escape; the native Popover API already handles those.

Under Rails with importmap-rails, the gem auto-pins the controller (no manual pin). Register it once, lazily:

// app/javascript/controllers/index.js
import { lazyLoadControllersFrom } from "@hotwired/stimulus-loading"
lazyLoadControllersFrom("daisy_ui/controllers", application)

Then opt in per call site:

Dropdown(:popover, :end, stimulus: true) do |dropdown|
  dropdown.button(:ghost, :sm) { "Actions" }
  dropdown.menu(:sm, class: "w-52") do |menu|
    menu.item { a(href: "#", role: "menuitem", tabindex: "-1") { "Edit" } }
  end
end
  • stimulus: true wires the daisy-dropdown controller (namespaced to avoid colliding with your own dropdown controller).
  • stimulus: "your-id" overrides the identifier.
  • The positioning fallback lazily imports @floating-ui/dom only when CSS anchor positioning is unavailable (Safari < 26, Firefox < 147), so modern browsers fetch nothing. The gem does not bundle it — if you enable the controller and need to support those browsers, pin it yourself (lazy, so modern browsers still skip it):
  # config/importmap.rb
  pin "@floating-ui/dom", to: "https://ga.jspm.io/npm:@floating-ui/dom@1.7.6/dist/floating-ui.dom.mjs", preload: false

If the pin is missing the menu still opens (native popover) — it just won't be repositioned on those legacy browsers, and the controller logs a console warning. Evergreen-only apps can skip the pin entirely.

  • Enable keyboard navigation with data: { daisy_dropdown_keyboard_value: true } on the dropdown. Roving focus targets role="menuitem" items, falling back to links/buttons in the menu.

JS-bundler (esbuild/vite/webpack) consumers: import the controller from the gem's app/javascript/daisy_ui/controllers/daisy_dropdown_controller.js and register it manually.

MCP Server (Claude Code Integration)

This gem includes an MCP (Model Context Protocol) server that provides component information to AI assistants like Claude Code.

Setup

Add to your Claude Code MCP settings (~/.claude.json or project .claude.json):

{
  "mcpServers": {
    "daisyui": {
      "command": "bundle",
      "args": ["exec", "daisyui-mcp"]
    }
  }
}

Available Tools

  • list_components - List all available DaisyUI components
  • get_component - Get detailed info about a specific component (modifiers, usage examples)
  • search_components - Search components by name or modifier

Usage

Refer to the docs to see how to use components. Here's an example:

Card :base_100 do |card|
  figure do
    img(src:)
  end
  card.body do
    card.title do
      "Shoes!"
    end
    p do
      "If a dog chews shoes whose shoes does he choose?"
    end
    card.actions class: "justify-end" do
      Button :primary do
        "Buy Now"
      end
    end
  end
end

Which produces:

Card example

Development

After checking out the repo, run bin/setup to install dependencies. Then, run bundle exec rspec to run the tests. You can also run bin/console for an interactive prompt that will allow you to experiment.

Contributing

Bug reports and pull requests are welcome on GitHub at https://github.com/mhenrixon/daisyui. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the code of conduct.

  1. Visit the docs to see which components are still not implemented or not yet added to the docs.

  2. Implement it.

  3. After your PR is merged, add it to the docs.

  4. Celebrate!

License

The gem is available as open source under the terms of the MIT License.

Code of Conduct

Everyone interacting in the DaisyUI project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the code of conduct.