Files
2026-07-13 12:49:11 +08:00

15 KiB

Creative Tim UI — Design & Usage Guide

Version 1.0.0 Creative Tim March 2026

Note: This document is primarily for AI agents and LLMs generating, reviewing, or modifying UI code that uses the Creative Tim UI block library. Guidance is optimised for automation and consistency. Humans may also find it useful as a reference.


Abstract

Creative Tim UI is a block library of 390+ React + Tailwind CSS components built on shadcn/ui. This guide covers the design philosophy behind every block, installation patterns, brand identity rules, Tailwind v4 constraints, and component conventions so AI-generated code stays consistent with the library — not just visually, but in the decisions it makes.


Table of Contents

  1. Design Philosophy
  2. Installing Blocks
  3. PRO Blocks & API Key
  4. Brand Identity
  5. Typography Rules
  6. Tailwind v4 Constraints
  7. Component Patterns
  8. Hydration Safety
  9. Block Authoring Guide

0. Design Philosophy

Across 300+ products and categories, Creative Tim has applied the same discipline: identify what developers actually need, build that exceptionally well, and cut everything else.

That discipline — research first, build only what's necessary, remove rather than add — is the foundation of every block in this library.

The five principles:

0.1 The 95% Rule

Build the common case exceptionally well. Do not reach for complex solutions to simple problems. A developer dropping in a block should not unwrap three layers of abstraction to change a label.

0.2 The "Light" Principle

A block is done not when there's nothing left to add, but when there's nothing left to remove. No feature flags for things that can be changed in code. No helper components used only once. No animations on elements that don't need to communicate state. No extra wrappers that exist only to satisfy a mental model.

0.3 Research Before Rendering

The first question is always: what problem does this solve for the person looking at the screen? Not what looks good in isolation. A pricing table reduces friction in a purchase decision. A dashboard surfaces the one number a person needs to act on. If the purpose isn't clear, the block isn't clear.

0.4 Standing on Foundations

shadcn/ui's Table, Card, Button, Badge, Dialog are well-designed, accessible, and maintained. Do not rebuild them. Do not wrap them unnecessarily. The Creative Tim contribution is the composition — how primitives are assembled into something useful for real work.

0.5 Real Use, Not Demo Use

The standard is: would a real team ship this to solve a real problem? Avoid visual complexity that doesn't carry information. A status badge that turns red when something is wrong is communication. A gradient that doesn't indicate anything is decoration. Only the first belongs.

Full detail with code examples: rules/brand-philosophy.md



1. Installing Blocks

1.1 Creative Tim CLI

The preferred method. Installs the block and all shadcn/ui primitive dependencies automatically.

# Initialize project (auto-detects Next.js / Vite / Remix / Astro)
npx @creative-tim/ui@latest init

# Add a block
npx @creative-tim/ui@latest add <block-name>

Package: @creative-tim/ui (v0.4.x+) Binaries: creative-tim-ui, creative-tim (aliases for the same CLI)

# All of these are equivalent:
npx @creative-tim/ui@latest add hero-01
npx creative-tim-ui add hero-01
npx creative-tim add hero-01

Examples:

npx @creative-tim/ui@latest add hero-01
npx @creative-tim/ui@latest add ai-agent-activity-01
npx @creative-tim/ui@latest add agent-management-list-01
npx @creative-tim/ui@latest add pricing-01

The CLI copies the component into components/ui/ (or your configured components directory) and runs npx shadcn@latest add for each dependency.

1.2 shadcn CLI (Full URL)

Use this when you prefer the shadcn workflow or when integrating with shadcn's registry tooling.

npx shadcn@latest add https://www.creative-tim.com/ui/r/<block-name>.json

Examples:

npx shadcn@latest add https://www.creative-tim.com/ui/r/hero-01.json
npx shadcn@latest add https://www.creative-tim.com/ui/r/testimonials-03.json

1.3 Block Discovery

Browse all blocks:

Individual block JSON (for programmatic access):

https://www.creative-tim.com/ui/r/<block-name>.json

2. PRO Blocks & API Key

PRO blocks require a Creative-Tim-Api-Key header when fetching the block JSON. Obtain a key at https://www.creative-tim.com/ui.

2.1 Environment Setup

# .env.local (never commit this)
CREATIVE_TIM_UI_API_KEY=pk_live_xxxxxxxxxxxxxxxx

2.2 CLI Install with Key

npx @creative-tim/ui@latest add <pro-block-name> --api-key $CREATIVE_TIM_UI_API_KEY

2.3 Global Login (persists across sessions)

npx @creative-tim/ui@latest login --api-key pk_live_xxxxxxxxxxxxxxxx

After login, all add commands automatically include the key — no --api-key flag needed.

2.4 Detecting PRO Blocks

PRO blocks are marked on the website. In the registry JSON they include "pro": true. Free-tier blocks have no pro field.


3. Brand Identity

3.1 Primary Accent — Orange

Rule: Use orange as the primary brand accent for interactive elements, icons, CTAs, and highlights.

// CORRECT — orange accent
<div className="flex h-8 w-8 items-center justify-center rounded-lg bg-orange-600">
  <Bot className="h-4 w-4 text-white" />
</div>
<AvatarFallback className="bg-orange-100 text-orange-700">JD</AvatarFallback>
<Badge className="bg-orange-50 text-orange-700 border-orange-200">OpenClaw</Badge>
// WRONG — violet/purple signals AI-generated defaults, not Creative Tim
<div className="bg-violet-600">...</div>
<Badge className="text-purple-700">...</Badge>

Why: Purple/violet is the default accent in many AI coding tools. Any block using it looks auto-generated and inconsistent with the library.

3.2 Status Colors

State Color
Active / Success emerald
Warning / Pending amber
Error / Stopped red
Info / In-Progress blue
Neutral / Inactive slate

3.3 Surface Colors

  • Card backgrounds: bg-background (default card) or bg-muted/30 (subtle areas like sidebars)
  • Borders: use border (theme token) — never hardcode border-gray-200
  • Text hierarchy: text-foreground (primary), text-muted-foreground (secondary)

4. Typography Rules

4.1 text-sm for Readable Content

Rule: Use text-sm for all body text that users need to read.

// CORRECT
<TableCell className="text-sm font-medium">{agent.name}</TableCell>
<p className="text-sm text-muted-foreground">{description}</p>
<span className="text-sm">{agent.lastActive}</span>
// WRONG — text-xs for readable body content is too small
<TableCell className="text-xs">{agent.name}</TableCell>
<p className="text-xs text-muted-foreground">{description}</p>

4.2 text-xs Only for Tiny/Supplementary Elements

text-xs is acceptable for:

  • Avatar initials (AvatarFallback)
  • font-mono timestamps and IDs
  • Badge/pill labels (priority, status)
  • Sidebar group headers (uppercase tracking-wider)
  • Action buttons with h-6 or h-7 (className="h-6 px-2 text-xs")

4.3 Never Use Arbitrary Font Sizes

// WRONG
<span className="text-[10px]">...</span>
<p className="text-[11px]">...</p>
<div className="text-[0.65rem]">...</div>

5. Tailwind v4 Constraints

5.1 No Arbitrary Values

Tailwind v4 uses a continuous numeric scale. Arbitrary bracket values are not allowed.

// WRONG — arbitrary values
<div className="h-[380px] w-[280px]">
<div className="max-w-[85%] min-w-[80px]">
<div className="gap-[14px] p-[18px]">
// CORRECT — use the numeric scale
<div className="h-96 w-72">         // h-96 = 384px, w-72 = 288px
<div className="max-w-sm">          // 384px — close enough, or max-w-xs / max-w-md
<div className="gap-3.5 p-4">       // gap-3.5 = 14px, p-4 = 16px

5.2 Numeric Scale Reference

h-6  = 24px     h-8  = 32px     h-10 = 40px
h-12 = 48px     h-16 = 64px     h-20 = 80px
h-24 = 96px     h-32 = 128px    h-48 = 192px
h-64 = 256px    h-80 = 320px    h-96 = 384px

gap-1 = 4px     gap-2 = 8px     gap-3 = 12px
gap-4 = 16px    gap-5 = 20px    gap-6 = 24px

p-2 = 8px       p-3 = 12px      p-4 = 16px
p-5 = 20px      p-6 = 24px

5.3 Use Semantic Width Utilities

// CORRECT
<div className="max-w-sm">    // 384px
<div className="max-w-md">    // 448px
<div className="max-w-lg">    // 512px
<div className="max-w-xl">    // 576px
<div className="max-w-2xl">   // 672px
<div className="w-64">        // sidebar = 256px
<div className="w-72">        // kanban column = 288px

5.4 Responsive Breakpoints Only

// CORRECT
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4">

// WRONG — custom media query
<div className="[@media(min-width:640px)]:grid-cols-2">

6. Component Patterns

6.1 Card Structure

Use Card > CardHeader + CardContent for content grouping.

import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"

<Card>
  <CardHeader>
    <CardTitle>Section Title</CardTitle>
  </CardHeader>
  <CardContent>
    {/* content */}
  </CardContent>
</Card>

6.2 KPI / Stat Cards — py-0

KPI cards use py-0 on CardContent to let the parent padding control vertical rhythm.

// CORRECT
<Card>
  <CardContent className="px-4 py-0">
    <p className="text-muted-foreground text-sm">Total Agents</p>
    <p className="text-2xl font-bold">42</p>
  </CardContent>
</Card>

// WRONG — default py causes too much padding in stat grids
<Card>
  <CardContent className="p-4">

6.3 Sidebar Navigation

Standard sidebar width is w-64 (256px), with bg-muted/30 border-r.

<aside className="bg-muted/30 flex h-full w-64 flex-col border-r">
  {/* logo row */}
  <div className="flex items-center gap-2.5 border-b px-5 py-4">
    <div className="flex h-8 w-8 items-center justify-center rounded-lg bg-orange-600">
      <Bot className="h-4 w-4 text-white" />
    </div>
    <span className="text-sm font-semibold">App Name</span>
  </div>
  {/* nav */}
  <nav className="flex-1 space-y-0.5 p-3">
    {NAV_ITEMS.map((item) => (
      <button
        key={item.label}
        className={`flex w-full items-center gap-3 rounded-md px-3 py-2 text-sm font-medium transition-colors ${
          item.active
            ? "bg-background text-foreground shadow-sm"
            : "text-muted-foreground hover:bg-background/60 hover:text-foreground"
        }`}
      >
        <item.icon className="h-4 w-4" />
        {item.label}
      </button>
    ))}
  </nav>
</aside>

6.4 ScrollArea for Overflow

Any list that may overflow its container must use ScrollArea.

import { ScrollArea } from "@/components/ui/scroll-area"

<ScrollArea className="flex-1">
  <div className="space-y-2 p-4">
    {items.map((item) => <Item key={item.id} {...item} />)}
  </div>
</ScrollArea>

6.5 Badge Variants

// Status badge (with dot)
<div className="inline-flex items-center gap-1.5 rounded-full border px-2 py-0.5 text-xs font-medium bg-emerald-50 text-emerald-700 border-emerald-200">
  <span className="h-1.5 w-1.5 rounded-full bg-emerald-500" />
  Active
</div>

// Type badge
<Badge variant="outline" className="text-xs font-medium bg-orange-50 text-orange-700 border-orange-200">
  OpenClaw
</Badge>

6.6 Avoid Inline Styles

// WRONG
<div style={{ color: '#ff6b35', padding: '16px' }}>

// CORRECT
<div className="text-orange-500 p-4">

7. Hydration Safety

7.1 Never Use Date in useState Initializer

new Date(), Date.now(), and toLocaleTimeString() return different values on the server vs. the client, causing React hydration errors.

// WRONG — causes "Hydration failed" error
const [events, setEvents] = useState(() => [
  { id: "1", timestamp: new Date().toLocaleTimeString() },
])

// WRONG — locale-dependent
const [time, setTime] = useState(new Date().toLocaleTimeString())
// CORRECT — static strings for initial SSR state
const INITIAL_TIMESTAMPS = ["10:30:00", "10:30:04", "10:30:08"]

const [events, setEvents] = useState(() =>
  INITIAL_DATA.slice(0, 3).map((e, i) => ({
    ...e,
    id: `init-${i}`,
    timestamp: INITIAL_TIMESTAMPS[i],
  }))
)

// Live timestamps only inside useEffect / setInterval (client-only)
function nowTimeString() {
  const d = new Date()
  return [d.getHours(), d.getMinutes(), d.getSeconds()]
    .map((n) => String(n).padStart(2, "0"))
    .join(":")
}

useEffect(() => {
  const interval = setInterval(() => {
    setEvents((prev) => [
      ...prev,
      { id: `evt-${Date.now()}`, timestamp: nowTimeString(), ... }
    ])
  }, 2000)
  return () => clearInterval(interval)
}, [])

7.2 suppressHydrationWarning for Intentional Mismatches

If a mismatch is truly intentional (e.g. a live clock that intentionally differs):

<time suppressHydrationWarning>{displayTime}</time>

8. Block Authoring Guide

8.1 File Structure

Every block consists of two files:

  1. registry/creative-tim/blocks/<name>.tsx — the component
  2. registry/creative-tim/blocks/<name>/page.tsx — the page wrapper

The page wrapper simply renders the block:

import BlockName from "@/registry/creative-tim/blocks/<name>"
export default function Page() {
  return <BlockName />
}

8.2 Registry Entry

Add to registry/registry-blocks.ts:

{
  name: "my-block-01",
  type: "registry:block",
  categories: ["my-category"],
  meta: {
    isFree: true,
    label: "My Block",
    createdAt: "2026-03-14",
  },
  registryDependencies: ["button", "card", "badge"],  // SHORT NAMES ONLY
  files: [
    {
      path: "registry/creative-tim/blocks/my-block-01.tsx",
      type: "registry:component",
    },
    {
      path: "registry/creative-tim/blocks/my-block-01/page.tsx",
      type: "registry:page",
      target: "app/blocks/my-block-01/page.tsx",
    },
  ],
}

IMPORTANT: registryDependencies must use short names ("button", "card", "badge") — never full URLs.

8.3 After Adding Blocks

Rebuild the registry:

pnpm --filter www registry:build

This regenerates public/r/*.json, __index__.tsx, and __blocks__.json (which powers llms.txt).

8.4 Import Paths in Blocks

Use the registry alias, not relative paths:

import { Button } from "@/registry/creative-tim/ui/button"
import { Card, CardContent } from "@/registry/creative-tim/ui/card"

8.5 Block Checklist

Before submitting a new block:

  • No violet or purple colors — use orange for brand accent
  • All readable text uses text-sm minimum
  • No arbitrary Tailwind values ([10px], [85%], etc.)
  • KPI/stat CardContent uses px-4 py-0
  • No new Date() in useState initializers
  • No inline style={{}} attributes
  • registryDependencies uses short names
  • registry:build completes without errors