Navigation controls for organizing content into switchable panels. Supports multiple sizes (xs, sm, md, lg, xl) and styles (box, border, lift) for creating intuitive content navigation and filtering interfaces.

Basic Tabs

Tabs are transparent by default and don't automatically do anything. Content-less tabs render as <button> elements you can wire up yourself; set the href attribute to render a link to another page instead.

Open Google
= daisy_tabs(css: "tabs-border") do |tabs|
  - tabs.with_tab(title: "I Do Nothing", active: true)
  - tabs.with_tab(title: "Ditto", html: { onclick: "alert('Ha! Gotcha ;-)')" })
  - tabs.with_tab(title: "Open Google", href: "https://google.com", target: "_blank")

Switchable Tabs (JavaScript)

Tabs with content but no href are automatically wired to the loco-tabs Stimulus controller (register it once from the npm package). The tablist follows the ARIA tabs pattern with manual activation: it is a single Tab stop, the Left / Right / Home / End keys move focus to browse, and Enter / Space (or a click) selects the focused tab.

Start the day with a strong cup of coffee.

A brisk walk beats the post-lunch slump.

Wind down with a good book.

= daisy_tabs(css: "tabs-lift") do |tabs|
  - tabs.with_tab(title: "Morning", active: true, content_wrapper_css: "bg-base-100 border-base-300 rounded-box p-6") do
    %p Start the day with a strong cup of coffee.
  - tabs.with_tab(title: "Afternoon", content_wrapper_css: "bg-base-100 border-base-300 rounded-box p-6") do
    %p A brisk walk beats the post-lunch slump.
  - tabs.with_tab(title: "Evening", content_wrapper_css: "bg-base-100 border-base-300 rounded-box p-6") do
    %p Wind down with a good book.

Tab Sizes

Tabs come in five different sizes: tabs-xs, tabs-sm, tabs-md (default), tabs-lg, and tabs-xl. Each size can be combined with any tab style.

Extra Small

Small

Medium (Default)

Large

Extra Large

.space-y-8
  .space-y-2
    %p.text-sm Extra Small
    = daisy_tabs(css: "tabs-border tabs-xs") do |tabs|
      - tabs.with_tab(title: "Tab 1", active: true)
      - tabs.with_tab(title: "Tab 2")
      - tabs.with_tab(title: "Tab 3")

  .space-y-2
    %p.text-sm Small
    = daisy_tabs(css: "tabs-border tabs-sm") do |tabs|
      - tabs.with_tab(title: "Tab 1", active: true)
      - tabs.with_tab(title: "Tab 2")
      - tabs.with_tab(title: "Tab 3")

  .space-y-2
    %p.text-sm Medium (Default)
    = daisy_tabs(css: "tabs-border") do |tabs|
      - tabs.with_tab(title: "Tab 1", active: true)
      - tabs.with_tab(title: "Tab 2")
      - tabs.with_tab(title: "Tab 3")

  .space-y-2
    %p.text-sm Large
    = daisy_tabs(css: "tabs-border tabs-lg") do |tabs|
      - tabs.with_tab(title: "Tab 1", active: true)
      - tabs.with_tab(title: "Tab 2")
      - tabs.with_tab(title: "Tab 3")

  .space-y-2
    %p.text-sm Extra Large
    = daisy_tabs(css: "tabs-border tabs-xl") do |tabs|
      - tabs.with_tab(title: "Tab 1", active: true)
      - tabs.with_tab(title: "Tab 2")
      - tabs.with_tab(title: "Tab 3")

Radio Button Tabs with Content

If you want the tabs to change their content, you can use the radio option. Note that this does not allow the use of icons or other HTML elements in the title. Radio tabs keep the browser's native radio keyboard behavior: arrow keys move the selection immediately.

Tab 1 content

Tab 2 content

Tab 3 content

= daisy_tabs(css: "tabs-lift w-[500px]", radio: true) do |tabs|
  - # Add some space before the first tab
  - tabs.with_tab(title: "", css: "w-14! cursor-auto!", disabled: true)

  - (1..3).each do |i|
    - tabs.with_tab(title: "Tab #{i}", content_wrapper_css: "bg-base-100 border-base-300 rounded-box p-6", checked: i == 1) do
      %p Tab #{i} content

Turbo Frame Tabs

You can also use tabs with Turbo Frames to load content dynamically.

2 Bars

2 bars content...

3 Bars

3 bars content...

4 Bars

4 bars content...

%turbo-frame#tabs--turbo-tabs-example
  = daisy_tabs(css: "tabs-lift w-full max-w-[500px]") do |tabs|
    - (2..4).each do |i|
      - is_checked = params[:custom_tab] ? params[:custom_tab] == i.to_s : i == 3
      - tabs.with_tab(href: "?custom_tab=#{i}", checked: is_checked) do |tab|
        - tab.with_title(css: "test-tab") do
          .flex.gap-x-2.items-center
            %span.size-4
              = loco_icon("bars-#{i}", css: "size-4")
            %span.whitespace-nowrap #{i} Bars

        - tab.with_custom_content(css: "tab-content bg-base-100 border-base-300 rounded-box p-6") do
          %p #{i} bars content...
Made with by Profoundry .
Copyright © 2023-2026 Profoundry LLC.
All rights reserved.