Base

Grid

Grid and GridItem wrap CSS grid with typed, fully-static Tailwind class maps for column counts, spans, and gaps at every breakpoint.

Installation

npx shadcn add https://rck.vibeboxph.com//r/grid.json

Live preview & controls

1
2
3
4
5
6
7
8
Controls
<Grid cols={4} gap={4}>
  <GridItem>1</GridItem>
  {/* … */}
</Grid>

Anatomy

  • GridThe grid container.
  • GridItemA grid child with an optional responsive column span.

Props

PropTypeDefaultDescription
cols1-1212Base column count.
colsSm / colsMd / colsLg / colsXl1-12Column count from each breakpoint up.
gap0 | 1 | 2 | 3 | 4 | 5 | 6 | 8 | 10 | 12 | 164Grid gap.
span (GridItem)1-12 | "full" | "auto"Base column span.
spanSm / spanMd / spanLg / spanXl (GridItem)1-12 | "full" | "auto"Column span from each breakpoint up.

Accessibility

  • Purely layout — renders plain <div> elements and doesn't alter document semantics or reading order.
  • Keep the DOM order matching the intended reading order; grid placement is visual only.

Source

registry/rck/ui/grid.tsx
import * as React from "react";
import { cn } from "@/lib/utils";

type ColCount = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12;
type Span = ColCount | "full" | "auto";
type Gap = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 8 | 10 | 12 | 16;

/**
 * Every responsive utility below is written as a full literal class name
 * (never built via string concatenation) so Tailwind's static analyzer can
 * always find it, whether this file lives in this repo or gets copied into
 * a consumer project.
 */
const colsMap: Record<ColCount, string> = {
  1: "grid-cols-1",
  2: "grid-cols-2",
  3: "grid-cols-3",
  4: "grid-cols-4",
  5: "grid-cols-5",
  6: "grid-cols-6",
  7: "grid-cols-7",
  8: "grid-cols-8",
  9: "grid-cols-9",
  10: "grid-cols-10",
  11: "grid-cols-11",
  12: "grid-cols-12",
};

const colsSmMap: Record<ColCount, string> = {
  1: "sm:grid-cols-1",
  2: "sm:grid-cols-2",
  3: "sm:grid-cols-3",
  4: "sm:grid-cols-4",
  5: "sm:grid-cols-5",
  6: "sm:grid-cols-6",
  7: "sm:grid-cols-7",
  8: "sm:grid-cols-8",
  9: "sm:grid-cols-9",
  10: "sm:grid-cols-10",
  11: "sm:grid-cols-11",
  12: "sm:grid-cols-12",
};

const colsMdMap: Record<ColCount, string> = {
  1: "md:grid-cols-1",
  2: "md:grid-cols-2",
  3: "md:grid-cols-3",
  4: "md:grid-cols-4",
  5: "md:grid-cols-5",
  6: "md:grid-cols-6",
  7: "md:grid-cols-7",
  8: "md:grid-cols-8",
  9: "md:grid-cols-9",
  10: "md:grid-cols-10",
  11: "md:grid-cols-11",
  12: "md:grid-cols-12",
};

const colsLgMap: Record<ColCount, string> = {
  1: "lg:grid-cols-1",
  2: "lg:grid-cols-2",
  3: "lg:grid-cols-3",
  4: "lg:grid-cols-4",
  5: "lg:grid-cols-5",
  6: "lg:grid-cols-6",
  7: "lg:grid-cols-7",
  8: "lg:grid-cols-8",
  9: "lg:grid-cols-9",
  10: "lg:grid-cols-10",
  11: "lg:grid-cols-11",
  12: "lg:grid-cols-12",
};

const colsXlMap: Record<ColCount, string> = {
  1: "xl:grid-cols-1",
  2: "xl:grid-cols-2",
  3: "xl:grid-cols-3",
  4: "xl:grid-cols-4",
  5: "xl:grid-cols-5",
  6: "xl:grid-cols-6",
  7: "xl:grid-cols-7",
  8: "xl:grid-cols-8",
  9: "xl:grid-cols-9",
  10: "xl:grid-cols-10",
  11: "xl:grid-cols-11",
  12: "xl:grid-cols-12",
};

const gapMap: Record<Gap, string> = {
  0: "gap-0",
  1: "gap-1",
  2: "gap-2",
  3: "gap-3",
  4: "gap-4",
  5: "gap-5",
  6: "gap-6",
  8: "gap-8",
  10: "gap-10",
  12: "gap-12",
  16: "gap-16",
};

export interface GridProps extends React.HTMLAttributes<HTMLDivElement> {
  /** Base column count (applies at every breakpoint unless overridden). */
  cols?: ColCount;
  /** Column count from the `sm` breakpoint up. */
  colsSm?: ColCount;
  /** Column count from the `md` breakpoint up. */
  colsMd?: ColCount;
  /** Column count from the `lg` breakpoint up. */
  colsLg?: ColCount;
  /** Column count from the `xl` breakpoint up. */
  colsXl?: ColCount;
  gap?: Gap;
}

export const Grid = React.forwardRef<HTMLDivElement, GridProps>(
  ({ className, cols = 12, colsSm, colsMd, colsLg, colsXl, gap = 4, ...props }, ref) => {
    return (
      <div
        ref={ref}
        data-slot="grid"
        className={cn(
          "grid",
          colsMap[cols],
          colsSm && colsSmMap[colsSm],
          colsMd && colsMdMap[colsMd],
          colsLg && colsLgMap[colsLg],
          colsXl && colsXlMap[colsXl],
          gapMap[gap],
          className,
        )}
        {...props}
      />
    );
  },
);
Grid.displayName = "Grid";

const spanMap: Record<Span, string> = {
  1: "col-span-1",
  2: "col-span-2",
  3: "col-span-3",
  4: "col-span-4",
  5: "col-span-5",
  6: "col-span-6",
  7: "col-span-7",
  8: "col-span-8",
  9: "col-span-9",
  10: "col-span-10",
  11: "col-span-11",
  12: "col-span-12",
  full: "col-span-full",
  auto: "col-auto",
};

const spanSmMap: Record<Span, string> = {
  1: "sm:col-span-1",
  2: "sm:col-span-2",
  3: "sm:col-span-3",
  4: "sm:col-span-4",
  5: "sm:col-span-5",
  6: "sm:col-span-6",
  7: "sm:col-span-7",
  8: "sm:col-span-8",
  9: "sm:col-span-9",
  10: "sm:col-span-10",
  11: "sm:col-span-11",
  12: "sm:col-span-12",
  full: "sm:col-span-full",
  auto: "sm:col-auto",
};

const spanMdMap: Record<Span, string> = {
  1: "md:col-span-1",
  2: "md:col-span-2",
  3: "md:col-span-3",
  4: "md:col-span-4",
  5: "md:col-span-5",
  6: "md:col-span-6",
  7: "md:col-span-7",
  8: "md:col-span-8",
  9: "md:col-span-9",
  10: "md:col-span-10",
  11: "md:col-span-11",
  12: "md:col-span-12",
  full: "md:col-span-full",
  auto: "md:col-auto",
};

const spanLgMap: Record<Span, string> = {
  1: "lg:col-span-1",
  2: "lg:col-span-2",
  3: "lg:col-span-3",
  4: "lg:col-span-4",
  5: "lg:col-span-5",
  6: "lg:col-span-6",
  7: "lg:col-span-7",
  8: "lg:col-span-8",
  9: "lg:col-span-9",
  10: "lg:col-span-10",
  11: "lg:col-span-11",
  12: "lg:col-span-12",
  full: "lg:col-span-full",
  auto: "lg:col-auto",
};

const spanXlMap: Record<Span, string> = {
  1: "xl:col-span-1",
  2: "xl:col-span-2",
  3: "xl:col-span-3",
  4: "xl:col-span-4",
  5: "xl:col-span-5",
  6: "xl:col-span-6",
  7: "xl:col-span-7",
  8: "xl:col-span-8",
  9: "xl:col-span-9",
  10: "xl:col-span-10",
  11: "xl:col-span-11",
  12: "xl:col-span-12",
  full: "xl:col-span-full",
  auto: "xl:col-auto",
};

export interface GridItemProps extends React.HTMLAttributes<HTMLDivElement> {
  span?: Span;
  spanSm?: Span;
  spanMd?: Span;
  spanLg?: Span;
  spanXl?: Span;
}

export const GridItem = React.forwardRef<HTMLDivElement, GridItemProps>(
  ({ className, span, spanSm, spanMd, spanLg, spanXl, ...props }, ref) => {
    return (
      <div
        ref={ref}
        data-slot="grid-item"
        className={cn(
          span && spanMap[span],
          spanSm && spanSmMap[spanSm],
          spanMd && spanMdMap[spanMd],
          spanLg && spanLgMap[spanLg],
          spanXl && spanXlMap[spanXl],
          className,
        )}
        {...props}
      />
    );
  },
);
GridItem.displayName = "GridItem";

Related examples

Browse the full examples gallery