Collapsible Rails Components

Show and hide content sections with smooth animations. Perfect for expanding cards, showing additional details, or creating interactive content areas that users can toggle on demand.

Installation

1. Stimulus Controller Setup

Start by adding the following controller to your project:

import { Controller } from "@hotwired/stimulus";

export default class extends Controller {
  static targets = ["content", "collapsedIcon", "expandedIcon"];
  static values = { open: Boolean };

  connect() {
    // Set initial state based on data attribute
    this.isOpen = this.openValue;
    this.updateDisplay();
  }

  toggle() {
    this.isOpen = !this.isOpen;
    this.updateDisplay();
  }

  updateDisplay() {
    const content = this.contentTarget;
    const collapsedIcon = this.collapsedIconTarget;
    const expandedIcon = this.expandedIconTarget;

    if (this.isOpen) {
      // Show content
      content.style.maxHeight = content.scrollHeight + "px";
      content.style.opacity = "1";
      content.setAttribute("data-state", "open");

      // Fade to expanded icon
      collapsedIcon.style.opacity = "0";
      expandedIcon.style.opacity = "1";

      // Update container state
      this.element.setAttribute("data-state", "open");
    } else {
      // Hide content
      content.style.maxHeight = "0";
      content.style.opacity = "0";
      content.setAttribute("data-state", "closed");

      // Fade to collapsed icon
      collapsedIcon.style.opacity = "1";
      expandedIcon.style.opacity = "0";

      // Update container state
      this.element.setAttribute("data-state", "closed");
    }
  }
}

Examples

Basic Collapsible

A simple collapsible component that shows/hides content with smooth animations. Features dual icons that automatically transition between collapsed and expanded states.

Rails developer starred 3 repositories

rails/rails
hotwired/stimulus
hotwired/turbo-rails
<div class="flex w-full justify-center">
  <div data-controller="collapsible" data-collapsible-open-value="false" data-state="closed" class="w-[350px] space-y-2">
    <!-- Header with toggle button -->
    <div class="flex items-center justify-between space-x-4">
      <h4 class="text-sm font-semibold">Rails developer starred 3 repositories</h4>

      <button type="button" class="relative flex items-center justify-center gap-1.5 rounded-lg bg-transparent p-1.5 -sm font-medium whitespace-nowrap text-neutral-800 transition-all duration-100 ease-in-out select-none hover:bg-neutral-100 hover:text-neutral-900 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-neutral-600 disabled:cursor-not-allowed disabled:opacity-50 dark:text-neutral-50 dark:hover:bg-neutral-600/50 dark:hover:text-white dark:focus-visible:outline-neutral-200" data-action="click->collapsible#toggle">
        <!-- Collapsed state icon (visible by default) -->

        <svg data-collapsible-target="collapsedIcon" xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 18 18" class="absolute transition-opacity duration-200 ease-in-out size-4.5" style="opacity: 0;">
          <g fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" stroke="currentColor">
            <polyline points="12.5 6.25 9 2.75 5.5 6.25"></polyline>
            <polyline points="12.5 11.75 9 15.25 5.5 11.75"></polyline>
          </g>
        </svg>

        <!-- Expanded state icon (hidden by default) -->
        <svg data-collapsible-target="expandedIcon" xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 18 18" class="transition-opacity duration-200 ease-in-out size-4.5" style="opacity: 0;">
          <g fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" stroke="currentColor">
            <polyline points="5.5 3.5 9 7 12.5 3.5"></polyline>
            <polyline points="5.5 14.5 9 11 12.5 14.5"></polyline>
          </g>
        </svg>

        <span class="sr-only">Toggle</span>
      </button>
    </div>

    <!-- Always visible content -->
    <div class="rounded-md border border-neutral-200 bg-neutral-50 dark:border-neutral-700 dark:bg-neutral-700/50 px-4 py-3 font-mono text-sm">rails/rails</div>

    <!-- Collapsible content -->
    <div data-collapsible-target="content" data-state="closed" class="space-y-2 overflow-hidden transition-all duration-300 ease-in-out" style="max-height: 0; opacity: 0;">
      <div class="rounded-md border border-neutral-200 bg-neutral-50 dark:border-neutral-700 dark:bg-neutral-700/50 px-4 py-3 font-mono text-sm">hotwired/stimulus</div>
      <div class="rounded-md border border-neutral-200 bg-neutral-50 dark:border-neutral-700 dark:bg-neutral-700/50 px-4 py-3 font-mono text-sm">hotwired/turbo-rails</div>
    </div>
  </div>
</div>

Configuration

The collapsible component is powered by a Stimulus controller that provides smooth animations and flexible configuration options for creating interactive content sections.

Controller Setup

Basic collapsible structure with required data attributes:

<div data-controller="collapsible" data-collapsible-open-value="false">
  <div class="flex items-center justify-between">
    <h4>Content Title</h4>
    <button data-action="click->collapsible#toggle">
      <svg data-collapsible-target="collapsedIcon"><!-- Icon for collapsed state --></svg>
      <svg data-collapsible-target="expandedIcon"><!-- Icon for expanded state --></svg>
    </button>
  </div>

  <div data-collapsible-target="content" class="overflow-hidden transition-all duration-300">
    <!-- Collapsible content goes here -->
  </div>
</div>

Configuration Values

Value Description Type Default
open
Controls the initial open/closed state when the component loads boolean false

Targets

Target Description Required
content
The collapsible content area that shows/hides with smooth animations Required
collapsedIcon
Icon element displayed when content is collapsed Optional
expandedIcon
Icon element displayed when content is expanded Optional

Actions

Action Description Usage
toggle
Toggles the collapsible content between open and closed states click->collapsible#toggle

Animation Features

  • Smooth Height Transitions: Content expands and collapses with smooth max-height animations
  • Opacity Fading: Content fades in/out during transitions for polished visual effects
  • Icon Switching: Automatic fade transitions between collapsed and expanded icon states
  • State Attributes: Automatic data-state attribute management for custom styling

Styling with CSS

The component automatically sets data-state attributes that you can use for custom styling:

  • Container: data-state="open" or data-state="closed"
  • Content: data-state="open" or data-state="closed"

Usage Tips

  • CSS Transitions: Add transition-all duration-300 to the content target for smooth animations
  • Overflow Hidden: Use overflow-hidden on the content target to prevent content from showing during animations
  • Icon Positioning: Use position: absolute on icon targets to overlay them for smooth transitions
  • Initial State: Set the initial data-collapsible-open-value based on your design needs

Table of contents

Get notified when new components come out