Collapsible Shared Partial

Don't forget the Stimulus controller!
The shared partial still requires the collapsible_controller.js Stimulus controller to be installed. Copy the controller from the Installation section on this page.

1. Copy shared partial files

Download all 1 shared partial files as a ZIP file.

Download all files (ZIP)

After downloading, unzip and copy the collapsible/ folder to your app/views/shared/components/ directory.

Copy these files to your app/views/shared/components/collapsible/ directory:

2. Usage example

<% collapsible_content = capture do %>
  <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>
<% end %>

<% always_visible_content = capture do %>
  <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>
<% end %>

<div class="flex w-fit justify-center">
  <%= render "shared/components/collapsible/collapsible",
    title: "Rails developer starred 3 repositories",
    icon: "arrows",
    visible_content: always_visible_content,
    content: collapsible_content %>
</div>

<% plus_minus_collapsible_content = capture do %>
  <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>
<% end %>

<% plus_minus_visible_content = capture do %>
  <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>
<% end %>

<div class="flex w-fit justify-center">
  <%= render "shared/components/collapsible/collapsible",
    title: "Rails developer starred 3 repositories",
    icon: "plus_minus",
    visible_content: plus_minus_visible_content,
    content: plus_minus_collapsible_content %>
</div>

Rendered usage example

Rails developer starred 3 repositories

rails/rails
hotwired/stimulus
hotwired/turbo-rails

Rails developer starred 3 repositories

rails/rails
hotwired/stimulus
hotwired/turbo-rails

Collapsible ViewComponent

Don't forget the Stimulus controller!
The ViewComponent still requires the collapsible_controller.js Stimulus controller to be installed. Copy the controller from the Installation section on this page.

1. Ensure the gem is installed

gem "view_component", "~> 4.2"

2. Copy component files

Download all 2 component files as a ZIP file.

Download all files (ZIP)

After downloading, unzip and copy the collapsible/ folder to your app/components/ directory.

Copy these files to your app/components/collapsible/ directory:

3. Usage example

<% always_visible_content = capture do %>
  <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>
<% end %>

<div class="flex w-fit justify-center">
  <%= render Collapsible::Component.new(
    title: "Rails developer starred 3 repositories",
    icon: :arrows,
    visible_content: always_visible_content
  ) do %>
    <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>
  <% end %>
</div>

<% plus_minus_visible_content = capture do %>
  <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>
<% end %>

<div class="flex w-fit justify-center">
  <%= render Collapsible::Component.new(
    title: "Rails developer starred 3 repositories",
    icon: :plus_minus,
    visible_content: plus_minus_visible_content
  ) do %>
    <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>
  <% end %>
</div>

Rendered usage example

Rails developer starred 3 repositories

rails/rails
hotwired/stimulus
hotwired/turbo-rails

Rails developer starred 3 repositories

rails/rails
hotwired/stimulus
hotwired/turbo-rails

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,
    animated: { type: Boolean, default: true },
  };

  connect() {
    this.isOpen = this.openValue;
    this.updateDisplay({ animate: false });
  }

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

  updateDisplay({ animate = true } = {}) {
    if (!this.hasContentTarget) return;

    const shouldAnimate =
      animate && this.animatedValue && !window.matchMedia("(prefers-reduced-motion: reduce)").matches;
    const content = this.contentTarget;

    if (this.isOpen) {
      content.style.maxHeight = shouldAnimate ? content.scrollHeight + "px" : "none";
      content.style.opacity = "1";
      content.setAttribute("data-state", "open");
      this.element.setAttribute("data-state", "open");
    } else {
      content.style.maxHeight = "0";
      content.style.opacity = "0";
      content.setAttribute("data-state", "closed");
      this.element.setAttribute("data-state", "closed");
    }

    this.updateIconDisplay();
    this.openValue = this.isOpen;
  }

  updateIconDisplay() {
    if (!this.hasCollapsedIconTarget || !this.hasExpandedIconTarget) return;

    const collapsedIcon = this.collapsedIconTarget;
    const expandedIcon = this.expandedIconTarget;
    collapsedIcon.style.transition = "none";
    expandedIcon.style.transition = "none";
    collapsedIcon.style.filter = "";
    expandedIcon.style.filter = "";

    if (this.isOpen) {
      collapsedIcon.style.opacity = "0";
      expandedIcon.style.opacity = "1";
    } else {
      collapsedIcon.style.opacity = "1";
      expandedIcon.style.opacity = "0";
    }
  }
}

Examples

Basic Collapsible

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

Rails developer starred 3 repositories

rails/rails
hotwired/stimulus
hotwired/turbo-rails
<div class="flex w-fit 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 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="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>

Collapsible with plus/minus icon

The same collapsible structure using plus/minus toggle icons.

Rails developer starred 3 repositories

rails/rails
hotwired/stimulus
hotwired/turbo-rails
<div class="flex w-fit 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 size-4.5" style="opacity: 0;">
          <g fill="currentColor">
            <path d="M14.75,9.75H3.25c-.414,0-.75-.336-.75-.75s.336-.75,.75-.75H14.75c.414,0,.75,.336,.75,.75s-.336,.75-.75,.75Z"></path>
            <path d="M9,15.5c-.414,0-.75-.336-.75-.75V3.25c0-.414,.336-.75,.75-.75s.75,.336,.75,.75V14.75c0,.414-.336,.75-.75,.75Z"></path>
          </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="size-4.5" style="opacity: 0;">
          <g fill="currentColor">
            <path d="M14.75,9.75H3.25c-.414,0-.75-.336-.75-.75s.336-.75,.75-.75H14.75c.414,0,.75,.336,.75,.75s-.336,.75-.75,.75Z"></path>
          </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" data-collapsible-animated-value="true">
  <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
animated
Enables/disables content transitions boolean true

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 instant switching 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 the overlaid icon target to keep both icon states aligned
  • Initial State: Set the initial data-collapsible-open-value based on your design needs
  • Disable Motion: Set data-collapsible-animated-value="false" to disable content transitions

Table of contents

Powered by

Get notified when new components come out