Collapsible

A component that allows users to expand or collapse content sections.

Docs
import { Collapsible, CollapsibleTrigger, CollapsibleContent } from "~/components/collapsible";

export default function CollapsibleBasicDemo() {
  return (
    <Collapsible>
      <CollapsibleTrigger>Click to expand</CollapsibleTrigger>
      <CollapsibleContent>
        <div class="pt-4 text-sm">Hidden content goes here.</div>
      </CollapsibleContent>
    </Collapsible>
  );
}

Installation

CLI

Run the following command to add the component to your project:

npx @ark-preset/cli@latest add collapsible

Manual

Create the recipe file at src/components/recipes/collapsible.ts:

import { tv, type VariantProps } from "tailwind-variants";

export const collapsibleVariants = tv({
  slots: {
    root: "w-full",
    trigger:
      "flex items-center justify-between gap-3 w-full p-2.5 text-sm font-medium border border-neutral-200 rounded-lg bg-transparent cursor-pointer hover:bg-accent focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring data-[disabled]:opacity-50 data-[disabled]:cursor-not-allowed",
    content:
      "overflow-hidden data-[state=open]:animate-accordion-expand data-[state=closed]:animate-accordion-collapse duration-500",
    indicator: "size-4 shrink-0 transition-transform duration-200 data-[state=open]:rotate-90",
  },
});

export type CollapsibleVariants = VariantProps<typeof collapsibleVariants>;

Create the component directory and files.

src/components/collapsible/collapsible.base.tsx:

import { Collapsible as ArkCollapsible } from "@ark-ui/solid/collapsible";
import { splitProps, type Component } from "solid-js";
import { collapsibleVariants } from "../recipes/collapsible";

const styles = collapsibleVariants();

const Root: Component<ArkCollapsible.RootProps> = (props) => {
  const [local, others] = splitProps(props, ["class"]);
  return <ArkCollapsible.Root class={styles.root({ class: local.class })} {...others} />;
};

const RootProvider: Component<ArkCollapsible.RootProviderProps> = (props) => {
  const [local, others] = splitProps(props, ["class"]);
  return <ArkCollapsible.RootProvider class={styles.root({ class: local.class })} {...others} />;
};

const Trigger: Component<ArkCollapsible.TriggerProps> = (props) => {
  const [local, others] = splitProps(props, ["class"]);
  return <ArkCollapsible.Trigger class={styles.trigger({ class: local.class })} {...others} />;
};

const Content: Component<ArkCollapsible.ContentProps> = (props) => {
  const [local, others] = splitProps(props, ["class"]);
  return <ArkCollapsible.Content class={styles.content({ class: local.class })} {...others} />;
};

const Indicator: Component<ArkCollapsible.IndicatorProps> = (props) => {
  const [local, others] = splitProps(props, ["class"]);
  return <ArkCollapsible.Indicator class={styles.indicator({ class: local.class })} {...others} />;
};

export const Collapsible = { Root, RootProvider, Trigger, Content, Indicator };

src/components/collapsible/index.tsx:

import { type Component } from "solid-js";
import { Collapsible as CollapsibleBase } from "./collapsible.base";
import { Collapsible as ArkCollapsible } from "@ark-ui/solid/collapsible";

const Collapsible = CollapsibleBase.Root;

const CollapsibleIndicator: Component<ArkCollapsible.IndicatorProps> = (props) => {
  return (
    <CollapsibleBase.Indicator {...props}>
      <svg
        xmlns="http://www.w3.org/2000/svg"
        width="16"
        height="16"
        viewBox="0 0 24 24"
        fill="none"
        stroke="currentColor"
        stroke-width="2"
        stroke-linecap="round"
        stroke-linejoin="round"
      >
        <path d="m6 9 6 6 6-6" />
      </svg>
    </CollapsibleBase.Indicator>
  );
};

const CollapsibleTrigger = CollapsibleBase.Trigger;
const CollapsibleContent = CollapsibleBase.Content;

export {
  Collapsible,
  CollapsibleTrigger,
  CollapsibleIndicator,
  CollapsibleContent,
  CollapsibleBase,
};

export { collapsibleVariants, type CollapsibleVariants } from "../recipes/collapsible";

Note: Make sure your project has the Tailwind CSS theme variables set up (--background, --foreground, --ring, --border, etc.) or override the utility classes to match your design system.

Usage

Basic Usage

import { Collapsible, CollapsibleTrigger, CollapsibleContent } from "~/components/collapsible";

export default function CollapsibleBasicDemo() {
  return (
    <Collapsible>
      <CollapsibleTrigger>Click to expand</CollapsibleTrigger>
      <CollapsibleContent>
        <div class="pt-4 text-sm">Hidden content goes here.</div>
      </CollapsibleContent>
    </Collapsible>
  );
}

With Indicator

The indicator rotates when the content is expanded. Use CollapsibleIndicator inside the trigger to show the chevron icon. The rotation is styled via the indicator slot in the recipe.

import {
  Collapsible,
  CollapsibleIndicator,
  CollapsibleTrigger,
  CollapsibleContent,
} from "~/components/collapsible";

export default function CollapsibleIndicatorDemo() {
  return (
    <Collapsible>
      <CollapsibleTrigger>
        Toggle Content
        <CollapsibleIndicator />
      </CollapsibleTrigger>
      <CollapsibleContent>
        <div class="pt-4 text-sm">The indicator automatically rotates when expanded.</div>
      </CollapsibleContent>
    </Collapsible>
  );
}

Advanced Usage

Root Provider

Use CollapsibleRootProvider when you need to access the collapsible state outside of the component tree. This pattern uses the useCollapsible hook from Ark UI to create a shared context that both the component and external elements can reference.

Open: true
The collapsible state is managed externally via useCollapsible. The output element above reads collapsible().open outside the collapsible tree.
import { useCollapsible } from "@ark-ui/solid/collapsible";
import {
  CollapsibleBase,
  CollapsibleIndicator,
  CollapsibleTrigger,
  CollapsibleContent,
} from "~/components/collapsible";

export default function CollapsibleRootProviderDemo() {
  const collapsible = useCollapsible({ defaultOpen: true });

  return (
    <div class="space-y-4">
      <output class="block text-sm text-muted-foreground">
        Open: {JSON.stringify(collapsible().open)}
      </output>

      <CollapsibleBase.RootProvider value={collapsible}>
        <CollapsibleTrigger>
          Click to expand
          <CollapsibleIndicator />
        </CollapsibleTrigger>
        <CollapsibleContent>
          <div class="pt-4 text-sm text-foreground">
            The collapsible state is managed externally via <code>useCollapsible</code>. The{" "}
            <code>output</code> element above reads <code>collapsible().open</code> outside the
            collapsible tree.
          </div>
        </CollapsibleContent>
      </CollapsibleBase.RootProvider>
    </div>
  );
}

The key difference:

  • Collapsible (Root) — manages its own state internally. Use for simple, self-contained usage.
  • CollapsibleRootProvider — accepts a pre-created context via useCollapsible. Use when you need to read or control the collapsible state from outside the component tree.

API Reference

See the Ark UI Collapsible documentation.