> ## Documentation Index
> Fetch the complete documentation index at: https://blode.co/dnd-grid/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Headless wrapper

Build a custom wrapper with useDndGrid.

<div className="not-prose my-6 rounded-lg border border-zinc-200/70 bg-white/70 shadow-sm dark:border-white/10 dark:bg-white/5">
  <iframe
    title="Headless wrapper preview"
    src="https://blode.co/dnd-grid/examples/headless-example?embed=1"
    className="h-[640px] w-full"
    loading="lazy"
  />
</div>

[View source on GitHub](https://github.com/mblode/dnd-grid/blob/main/apps/web/examples/dnd-grid-headless-example.tsx)

## Installation

### CLI

```bash
npx shadcn@latest add https://blode.co/dnd-grid/r/headless-example.json
```
### Manual

```bash
npm install @dnd-grid/react
```

```css
@import "@dnd-grid/react/styles.css";
```

```tsx title="components/dnd-grid-headless-example.tsx"
"use client";

import {
  GridItem,
  type Layout,
  useContainerWidth,
  useDndGrid,
} from "@dnd-grid/react";
import { Children, type MutableRefObject, useCallback, useState } from "react";

const initialLayout: Layout = [
  { id: "a", x: 0, y: 0, w: 3, h: 2 },
  { id: "b", x: 3, y: 0, w: 3, h: 2 },
  { id: "c", x: 6, y: 0, w: 3, h: 2 },
];

export function HeadlessExample() {
  const [layout, setLayout] = useState(initialLayout);
  const { width, containerRef, mounted } = useContainerWidth({
    measureBeforeMount: true,
  });
  const children = layout.map((item) => (
    <div className="grid-item" key={item.id}>
      {item.id}
    </div>
  ));

  const { gridProps, itemProps, liveRegionElement } = useDndGrid({
    layout,
    onLayoutChange: setLayout,
    cols: 12,
    rowHeight: 50,
    width,
    children,
  });

  const { ref: gridRef, ...restGridProps } = gridProps;
  const setGridRef = useCallback(
    (node: HTMLDivElement | null) => {
      containerRef.current = node;
      if (typeof gridRef === "function") {
        gridRef(node);
        return;
      }
      if (gridRef) {
        (gridRef as MutableRefObject<HTMLDivElement | null>).current = node;
      }
    },
    [containerRef, gridRef]
  );

  const droppingProps = itemProps.getDroppingItemProps();
  const placeholderProps = itemProps.getPlaceholderProps();

  return (
    <div className="space-y-2">
      <div className="text-xs text-zinc-500">Custom wrapper</div>
      <div {...restGridProps} ref={setGridRef}>
        {mounted && (
          <>
            {liveRegionElement}
            {Children.map(children, (child) => {
              const props = itemProps.getItemProps(child);
              return props ? <GridItem {...props} /> : null;
            })}
            {droppingProps && <GridItem {...droppingProps} />}
            {placeholderProps && <GridItem {...placeholderProps} />}
          </>
        )}
      </div>
    </div>
  );
}
```

## Usage

```tsx
import { HeadlessExample } from "@/components/dnd-grid-headless-example";

export default function Page() {
  return <HeadlessExample />;
}
```