Skip to content
DocsSupportPricing
Roadmap (has updates coming soon)XFigma3.2K
Overview
  • Introduction
  • Get Started
  • License Setup
  • Styling
  • Registry
  • MCP Server
  • Agent Skills
  • Changelog
MCP Server
  • Claude
  • CodexCodexCodex
  • Cursor
  • v0
  • Lovable
  • Replit
  • OpenCode
  • VS Code
  • Zed
Components
  • Alert
  • Autocomplete
  • Badge
  • Data GridAutosizing, fill and resize hardening, React Compiler ready
  • Date Selector
  • Event CalendarNew event calendar component with five views
  • File Upload
  • FiltersSizing and interaction refinements
  • Frame
  • GanttNew gantt component with day to year scales
  • Icon Stack
  • Icon TileNew icon tile component with five surface variants
  • KanbanonValueCommit callback and accessibility improvements
  • Number Field
  • Phone Input
  • Rating
  • Scrollspy
  • SortableonValueCommit callback and accessibility improvements
  • StepperRender prop composition and styling refinements
  • Timeline
  • Tree

Application

  • App Shell
  • Auth
  • Card
  • Chart
  • Dashboard
  • Dialog
  • Empty State
  • Event CalendarNew event calendar block added
  • Form
  • GanttNew gantt block added
  • Kanban Board
  • List
  • Navbar
  • Onboarding
  • Profile
  • Schedule
  • Settings
  • Sheet
  • Stats
  • Timeline
  • Wizard

Solutions

  • Agents
  • AI Ops
  • Analytics
  • Billing
  • Bookings
  • CRM
  • Inventory
  • Users

eCommerce

  • Category Card
  • Checkout
  • Comparison
  • Coupon
  • Filter Sidebar
  • Product Card
  • Product Detail
  • Product Grid
  • Receipt
  • Review
  • Shopping Cart
  • Wishlist

Data Grid

  • Base
  • Columns
  • Drag & Drop
  • EditingNew editable Data Grid block added
  • Expansion
  • Filtering
  • Grouping
  • Virtualization

Marketing

  • Blog
  • Contact
  • CTA
  • FAQ

Resources

  • Components
  • Blocks
  • Docs
  • Support
  • Pricing
  • Roadmap(has updates coming soon)
  • AffiliateSoon

Legal

  • Privacy Policy
  • Terms & Conditions
  • License
  • Refunds
  • Cookies

© 2026ReUI · All rights reserved

3.2K

Shadcn Icon Tile

PreviousNext

Custom Shadcn Icon Tile for React and Tailwind CSS. Shadcn Icon Tile that wraps an icon in a consistent square surface for list rows, feature cards, and empty states.

Base UIRadix UI

Installation

pnpm dlx shadcn@latest add @reui/icon-tile

Usage

import { FolderIcon } from "lucide-react"
 
import { IconTile } from "@/components/reui/icon-tile"

Shadcn Icon Tile Free Components

Browse 17 production-ready Shadcn Icon Tile components for dashboards, forms, and product UI. These examples use Base UI primitives from @base-ui/react and stay fully compatible with Shadcn Create so radius, color, and typography match your configured theme.

Browse all 17 Shadcn Icon Tile components for copy-ready layouts, dashboards, and forms built with Tailwind CSS in the ReUI library.

Icon StackKanban

On This Page

InstallationUsageExamplesElevated icon tileFramed icon tileAll variantsSizesCircular tilesCustom sizingColor tonesText contentList rowFeature cardEmpty stateInteractive tileStatus overlaySoft tonesSolid tonesBrand colorsAPI ReferenceVariantsSizesRadiusCompositionData attributesCSS VariablesOverriding the sizeProps
<IconTile aria-hidden="true"> <FolderIcon /> </IconTile>

IconTile is a shadcn-compatible ReUI primitive that gives an icon a square surface with a consistent size, radius, and border treatment. Use it for list row media, feature cards, settings rows, empty states, and any place where a bare glyph needs a container to sit in.

The tile sizes its own child svgs, so you do not pass a size class to the icon in the common case. Keep meaningful labels in the surrounding copy and mark purely decorative tiles with aria-hidden="true".

Examples

Elevated icon tile

import { IconTile } from "@/components/reui/icon-tile"
import { PackageIcon } from 'lucide-react'

export function Pattern() {
  return (
    <div className="flex items-center justify-center">
      <IconTile variant="elevated" aria-hidden="true">
        <PackageIcon />
      </IconTile>
    </div>
  )
}

Framed icon tile

import { IconTile } from "@/components/reui/icon-tile"
import { LayoutDashboardIcon } from 'lucide-react'

export function Pattern() {
  return (
    <div className="flex items-center justify-center">
      <IconTile variant="frame" aria-hidden="true">
        <LayoutDashboardIcon />
      </IconTile>
    </div>
  )
}

All variants

import { IconTile } from "@/components/reui/icon-tile"
import { PackageIcon } from 'lucide-react'

const variants = ["outline", "elevated", "soft", "solid", "frame"] as const

export function Pattern() {
  return (
    <div className="flex items-center justify-center gap-4">
      {variants.map((variant) => (
        <IconTile key={variant} variant={variant} aria-hidden="true">
          <PackageIcon />
        </IconTile>
      ))}
    </div>
  )
}

Sizes

import { IconTile } from "@/components/reui/icon-tile"
import { PackageIcon } from 'lucide-react'

const sizes = ["xs", "sm", "default", "lg", "xl"] as const

export function Pattern() {
  return (
    <div className="flex items-end justify-center gap-4">
      {sizes.map((size) => (
        <IconTile key={size} variant="elevated" size={size} aria-hidden="true">
          <PackageIcon />
        </IconTile>
      ))}
    </div>
  )
}

Circular tiles

import { IconTile } from "@/components/reui/icon-tile"
import { CircleCheckIcon, GlobeIcon, StarIcon } from 'lucide-react'

export function Pattern() {
  return (
    <div className="flex items-center justify-center gap-4">
      <IconTile variant="outline" size="lg" radius="full" aria-hidden="true">
        <GlobeIcon />
      </IconTile>
      <IconTile variant="elevated" size="lg" radius="full" aria-hidden="true">
        <StarIcon />
      </IconTile>
      <IconTile variant="frame" size="lg" radius="full" aria-hidden="true">
        <CircleCheckIcon />
      </IconTile>
    </div>
  )
}

Custom sizing

import { IconTile } from "@/components/reui/icon-tile"
import { ImageIcon, Settings2Icon } from 'lucide-react'

export function Pattern() {
  return (
    <div className="flex items-center justify-center gap-4">
      <IconTile variant="elevated" className="size-14" aria-hidden="true">
        <ImageIcon  className="size-7" />
      </IconTile>
      <IconTile
        variant="elevated"
        className="[--icon-tile-icon-size:--spacing(7)] [--icon-tile-size:--spacing(14)]"
        aria-hidden="true"
      >
        <Settings2Icon />
      </IconTile>
    </div>
  )
}

Color tones

import { IconTile } from "@/components/reui/icon-tile"
import { CircleAlertIcon, CircleCheckIcon, ClockIcon, StarIcon } from 'lucide-react'

export function Pattern() {
  return (
    <div className="flex items-center justify-center gap-3">
      <IconTile
        aria-hidden="true"
        className="border-primary/10 bg-primary/10 text-primary dark:border-primary/25 dark:bg-primary/15"
      >
        <StarIcon />
      </IconTile>
      <IconTile
        aria-hidden="true"
        className="border-success/15 bg-success/10 text-success-foreground dark:border-success/25 dark:bg-success/15 dark:text-success"
      >
        <CircleCheckIcon />
      </IconTile>
      <IconTile
        aria-hidden="true"
        className="border-warning/15 bg-warning/10 text-warning-foreground dark:border-warning/25 dark:bg-warning/15 dark:text-warning"
      >
        <ClockIcon />
      </IconTile>
      <IconTile
        aria-hidden="true"
        className="border-destructive/15 bg-destructive/10 text-destructive-foreground dark:border-destructive/25 dark:bg-destructive/15 dark:text-destructive"
      >
        <CircleAlertIcon />
      </IconTile>
    </div>
  )
}

Text content

import { IconTile } from "@/components/reui/icon-tile"

export function Pattern() {
  return (
    <div className="flex items-center justify-center gap-3">
      <IconTile variant="elevated" className="text-xs font-medium">
        AK
      </IconTile>
      <IconTile
        variant="outline"
        radius="full"
        className="text-muted-foreground text-xs font-medium"
      >
        12
      </IconTile>
    </div>
  )
}

List row

import { IconTile } from "@/components/reui/icon-tile"

import {
  Item,
  ItemContent,
  ItemDescription,
  ItemMedia,
  ItemTitle,
} from "@/components/ui/item"
import { FileTextIcon } from 'lucide-react'

export function Pattern() {
  return (
    <div className="flex w-full max-w-sm items-center justify-center">
      <Item variant="outline">
        <ItemMedia>
          <IconTile variant="elevated" size="sm" aria-hidden="true">
            <FileTextIcon />
          </IconTile>
        </ItemMedia>
        <ItemContent>
          <ItemTitle>Quarterly report</ItemTitle>
          <ItemDescription>Updated 2 hours ago by Anna</ItemDescription>
        </ItemContent>
      </Item>
    </div>
  )
}

Feature card

import { Frame, FramePanel } from "@/components/reui/frame"
import { IconTile } from "@/components/reui/icon-tile"
import { LayoutDashboardIcon } from 'lucide-react'

export function Pattern() {
  return (
    <Frame className="w-full max-w-xs">
      <FramePanel className="flex flex-col gap-3">
        <IconTile variant="elevated" size="lg" aria-hidden="true">
          <LayoutDashboardIcon />
        </IconTile>
        <div className="flex flex-col gap-1">
          <h3 className="text-sm font-medium">Unified dashboard</h3>
          <p className="text-muted-foreground text-xs">
            Track every project, deployment and metric from a single workspace.
          </p>
        </div>
      </FramePanel>
    </Frame>
  )
}

Empty state

import { IconTile } from "@/components/reui/icon-tile"

import {
  Empty,
  EmptyDescription,
  EmptyHeader,
  EmptyMedia,
  EmptyTitle,
} from "@/components/ui/empty"
import { FolderIcon } from 'lucide-react'

export function Pattern() {
  return (
    <div className="flex items-center justify-center">
      <Empty>
        <EmptyHeader>
          <EmptyMedia>
            <IconTile variant="frame" size="xl" aria-hidden="true">
              <FolderIcon />
            </IconTile>
          </EmptyMedia>
          <EmptyTitle>No files yet</EmptyTitle>
          <EmptyDescription>
            Upload a file to get started. Everything you add shows up here.
          </EmptyDescription>
        </EmptyHeader>
      </Empty>
    </div>
  )
}

Interactive tile

import { IconTile } from "@/components/reui/icon-tile"
import { Settings2Icon } from 'lucide-react'

export function Pattern() {
  return (
    <div className="flex items-center justify-center">
      <IconTile
        render={<a href="#" aria-label="Open settings" />}
        variant="elevated"
        className="hover:bg-accent focus-visible:ring-ring focus-visible:ring-offset-background transition-colors outline-none focus-visible:ring-2 focus-visible:ring-offset-2"
      >
        <Settings2Icon  aria-hidden="true" />
      </IconTile>
    </div>
  )
}

Status overlay

import { IconTile } from "@/components/reui/icon-tile"
import { GlobeIcon } from 'lucide-react'

export function Pattern() {
  return (
    <div className="flex items-center justify-center">
      <span className="relative inline-flex" aria-hidden="true">
        <IconTile variant="elevated">
          <GlobeIcon />
        </IconTile>
        <span className="border-background bg-success absolute -end-0.5 -top-0.5 size-3 rounded-full border-2" />
      </span>
    </div>
  )
}

Soft tones

import { IconTile } from "@/components/reui/icon-tile"
import { StarIcon } from 'lucide-react'

// The `soft` variant derives every fill and border from currentColor, so one
// text color class retints the whole tile. An empty class keeps the primary
// default. The tones pair a readable light-mode foreground with the brighter
// dark-mode token, matching the ReUI badge light variants.
const tones = [
  "",
  "text-info-foreground dark:text-info",
  "text-success-foreground dark:text-success",
  "text-warning-foreground dark:text-warning",
  "text-destructive-foreground dark:text-destructive",
]

export function Pattern() {
  return (
    <div className="flex items-center justify-center gap-4">
      {tones.map((tone) => (
        <IconTile
          key={tone || "primary"}
          variant="soft"
          className={tone}
          aria-hidden="true"
        >
          <StarIcon />
        </IconTile>
      ))}
    </div>
  )
}

Solid tones

import { IconTile } from "@/components/reui/icon-tile"
import { CircleCheckIcon } from 'lucide-react'

// The `solid` variant is a filled tone with a contrasting glyph. Override the
// fill with a `bg-*` class plus a readable text color. An empty class keeps the
// primary default.
const tones = [
  "",
  "bg-info text-white",
  "bg-success text-white",
  "bg-warning text-white",
  "bg-destructive text-white",
]

export function Pattern() {
  return (
    <div className="flex items-center justify-center gap-4">
      {tones.map((tone) => (
        <IconTile
          key={tone || "primary"}
          variant="solid"
          className={tone}
          aria-hidden="true"
        >
          <CircleCheckIcon />
        </IconTile>
      ))}
    </div>
  )
}

Brand colors

import { IconTile } from "@/components/reui/icon-tile"
import { CircleAlertIcon, MessageSquareIcon, Settings2Icon, StarIcon } from 'lucide-react'

// The `elevated` variant keeps its background-colored ring and shadow while the
// fill is overridden with a raw Tailwind color plus a white glyph, for solid
// brand-colored tiles.
const tiles = [
  {
    className: "bg-indigo-500 text-white",
    icon: (
      <StarIcon />
    ),
  },
  {
    className: "bg-amber-500 text-white",
    icon: (
      <MessageSquareIcon />
    ),
  },
  {
    className: "bg-emerald-600 text-white",
    icon: (
      <Settings2Icon />
    ),
  },
  {
    className: "bg-rose-500 text-white",
    icon: (
      <CircleAlertIcon />
    ),
  },
]

export function Pattern() {
  return (
    <div className="flex items-center justify-center gap-4">
      {tiles.map((tile) => (
        <IconTile
          key={tile.className}
          variant="elevated"
          className={tile.className}
          aria-hidden="true"
        >
          {tile.icon}
        </IconTile>
      ))}
    </div>
  )
}

API Reference

IconTile renders a single element (a span by default) that centers its children on a sized, rounded surface. There are no sub-components. Everything the tile draws is derived from CSS variables set on that root, so a tile can be retuned from one className without extra wrappers.

Implementation dependencies: @base-ui/react and class-variance-authority.

Variants

VariantDescription
outlineThe default. A plain bordered surface on the page background, quiet enough for list rows and toolbars.
elevatedA muted fill with a 2px background-colored ring and a soft shadow, the same recipe as the card-2 block. Reads as a physical chip lifted off the page.
softA tinted double container: an opacity-filled outer ring with no border around a bordered inner card, all from currentColor. Defaults to text-primary; set a text color class to retint the whole tile.
solidA filled tone with a contrasting glyph. Defaults to bg-primary; override the fill with a bg-* class plus a readable text color to retint.
frameA double container: a muted ring around an inset card, matching the Frame primitive. The inner card is painted with an ::after pseudo element, so there is no wrapper node and render composition keeps working.
<IconTile variant="elevated">
  <PackageIcon />
</IconTile>

Sizes

Each size sets the tile box, the glyph size, and the inset shared by the soft and frame variants together, so the proportions hold at every step.

SizeTileGlyph
xs24px14px
sm32px16px
default40px18px
lg48px22px
xl56px28px
<IconTile size="lg" variant="frame">
  <FolderIcon />
</IconTile>

Radius

radius="default" follows the active style radius, so a tile matches the rest of the installed style. radius="full" makes the tile circular.

<IconTile radius="full">
  <FolderIcon />
</IconTile>

Composition

The tile composes through the Base UI render prop, so it can become a link, a button, or any other element while keeping its surface styles and data attributes.

<IconTile render={<a href="/settings" aria-label="Open settings" />}>
  <Settings2Icon aria-hidden="true" />
</IconTile>

Data attributes

The root carries data attributes for styling and for test selectors.

AttributeValue
data-slot"icon-tile"
data-variant"outline" | "elevated" | "soft" | "solid" | "frame"
data-size"xs" | "sm" | "default" | "lg" | "xl"

CSS Variables

The root sets four variables, so a single className can retune the whole tile without touching the icon.

VariableDefault (at size="default")Description
--icon-tile-size--spacing(10)Tile width and height.
--icon-tile-icon-size--spacing(4.5)Glyph size applied to child svgs that carry no size-* class.
--icon-tile-radiusActive style radiusCorner radius. Also drives the inner card of the soft and frame variants.
--icon-tile-inset--spacing(0.75)Gap between the outer ring and the inner card of the soft and frame variants.

Overriding the size

There are three ways to step outside the size scale, and they can be combined.

  1. A plain utility on the tile. A size-* class in className wins over the primitive's size-(--icon-tile-size) through tailwind-merge, so the tile box changes while the glyph keeps the size from the size prop.

    <IconTile variant="elevated" className="size-14">
      <ImageIcon />
    </IconTile>
  2. A size-* class directly on the child icon. Child svgs are auto-sized with [&_svg:not([class*=size-])]:size-(--icon-tile-icon-size), so an icon that already carries a size-* class opts out of the tile's glyph sizing.

    <IconTile className="size-14">
      <ImageIcon className="size-7" />
    </IconTile>
  3. The CSS variables. Setting --icon-tile-size and --icon-tile-icon-size keeps the tile and glyph in step and needs no class on the icon. The soft and frame inset stays on the value from the size prop, so set --icon-tile-inset too when such a tile moves far from its scale step.

    <IconTile className="[--icon-tile-icon-size:--spacing(7)] [--icon-tile-size:--spacing(14)]">
      <Settings2Icon />
    </IconTile>

For color, reach for the soft or solid variant and set a single text color class; both derive their fills and borders from currentColor. Any variant can also be recolored directly through className, as the brand-color example does by overriding the elevated fill with a Tailwind color.

<IconTile variant="soft" className="text-success">
  <CircleCheckIcon />
</IconTile>

Props

PropTypeDefaultDescription
variant"outline" | "elevated" | "soft" | "solid" | "frame""outline"The surface treatment of the tile.
size"xs" | "sm" | "default" | "lg" | "xl""default"The tile size, which also sets the glyph size.
radius"default" | "full""default""default" uses the active style radius, "full" makes the tile circular.
renderuseRender.RenderProp-Render a different element while keeping the tile styles (Base UI useRender).
classNamestring-Additional CSS classes, including overrides of the tile CSS variables.
childrenReact.ReactNode-The icon, initials, or short text rendered inside the tile.

All native span props are supported.

import { IconTile } from "@/components/reui/icon-tile"
import { FolderIcon } from 'lucide-react'

export function Pattern() {
  return (
    <div className="flex items-center justify-center">
      <IconTile aria-hidden="true">
        <FolderIcon />
      </IconTile>
    </div>
  )
}