Documentation
Three ways to get a block into your project: copy-paste, the shadcn CLI, or your AI editor via MCP.
Requirements
Blocks are plain React + Tailwind. You need a project with React 19, Tailwind CSS v4 and shadcn/ui initialized (`npx shadcn init`). Every block is standalone TypeScript — no runtime, no wrapper library.
1. Copy-paste
Open any block in the catalog, switch to the Code tab, copy, paste. Free blocks are open; pro blocks unlock after signing in with your purchase email.
2. shadcn CLI
Every block installs through the @blockforge namespace — dependencies and ui components resolve automatically:
npx shadcn@latest add @blockforge/hero-centered
One-time setup: add the registry to your components.json — the token from your account (exported as BLOCKFORGE_TOKEN) unlocks the pro blocks; free blocks need no token:
{
"registries": {
"@blockforge": {
"url": "https://blockforge.terravidhal.me/r/{name}.json",
"headers": {
"Authorization": "Bearer ${BLOCKFORGE_TOKEN}"
}
}
}
}Raw URLs work too, without any setup — handy for one-off installs of free blocks:
npx shadcn@latest add https://blockforge.terravidhal.me/r/hero-centered.json
3. MCP — for AI editors
Claude Code, Cursor and friends can search blocks, read source and install pages without leaving the editor.
claude mcp add --transport http blockforge https://blockforge.terravidhal.me/mcp
Theming
Every block uses semantic shadcn tokens only — bg-background, text-muted-foreground, border-border… Change your CSS variables once and every block follows, dark mode included. No hard-coded colors, anywhere.
CSS utilities
Some blocks use Blockforge's signature utilities (blueprint grids, marquee, orbit, border beam…). Installing via the shadcn CLI injects the needed ones automatically. If you copy-paste instead, append what you need from the full set below to your global CSS:
@utility bg-grid-lg {
background-image: linear-gradient(to right, color-mix(in oklab, var(--foreground) 9%, transparent) 1px, transparent 1px), linear-gradient(to bottom, color-mix(in oklab, var(--foreground) 9%, transparent) 1px, transparent 1px);
background-size: 96px 96px;
}
@utility bg-grid-sm {
background-image: linear-gradient(to right, color-mix(in oklab, var(--foreground) 4%, transparent) 1px, transparent 1px), linear-gradient(to bottom, color-mix(in oklab, var(--foreground) 4%, transparent) 1px, transparent 1px);
background-size: 24px 24px;
}
@utility bg-dots {
background-image: radial-gradient(circle, color-mix(in oklab, var(--foreground) 16%, transparent) 1px, transparent 1px);
background-size: 14px 14px;
}
@utility bg-stripes {
background-image: repeating-linear-gradient(-45deg, color-mix(in oklab, var(--foreground) 5%, transparent) 0 1px, transparent 1px 7px);
}
@utility animate-marquee {
animation: marquee 32s linear infinite;
@media (prefers-reduced-motion: reduce) {
animation: none;
}
}
@utility animate-marquee-reverse {
animation: marquee 32s linear infinite reverse;
@media (prefers-reduced-motion: reduce) {
animation: none;
}
}
@utility animate-caret {
animation: caret-blink 1.1s steps(1) infinite;
@media (prefers-reduced-motion: reduce) {
animation: none;
}
}
@utility animate-status {
animation: status-pulse 2.4s ease-out infinite;
@media (prefers-reduced-motion: reduce) {
animation: none;
}
}
@utility animate-orbit {
animation: orbit 40s linear infinite;
@media (prefers-reduced-motion: reduce) {
animation: none;
}
}
@utility animate-orbit-reverse {
animation: orbit 40s linear infinite reverse;
@media (prefers-reduced-motion: reduce) {
animation: none;
}
}
@utility animate-word-cycle {
animation: word-cycle var(--cycle-duration, 8s) infinite;
animation-delay: var(--cycle-delay, 0s);
animation-fill-mode: both;
@media (prefers-reduced-motion: reduce) {
animation: none;
opacity: 0;
&:first-child {
opacity: 1;
transform: none;
}
}
}
@utility border-beam {
position: relative;
&::after {
content: "";
position: absolute;
inset: -1px;
border-radius: inherit;
padding: 1px;
background: conic-gradient(from var(--beam-angle), transparent 0deg 300deg, color-mix(in oklab, var(--foreground) 90%, transparent) 350deg, transparent 360deg);
mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
mask-composite: exclude;
animation: beam-rotate 3.2s linear infinite;
pointer-events: none;
}
@media (prefers-reduced-motion: reduce) {
&::after {
display: none;
}
}
}
@utility beam-hover {
&::after {
opacity: 0;
transition: opacity 0.25s ease-out;
animation-play-state: paused;
}
&:hover::after, &:focus-within::after {
opacity: 1;
animation-play-state: running;
}
}
@utility flux-x {
position: absolute;
height: 1px;
width: 9rem;
background: linear-gradient(90deg, transparent, color-mix(in oklab, var(--foreground) 65%, transparent), transparent);
animation: flux-x var(--flux-duration, 8s) linear infinite;
animation-delay: var(--flux-delay, 0s);
}
@utility flux-y {
position: absolute;
width: 1px;
height: 9rem;
background: linear-gradient(180deg, transparent, color-mix(in oklab, var(--foreground) 65%, transparent), transparent);
animation: flux-y var(--flux-duration, 9s) linear infinite;
animation-delay: var(--flux-delay, 0s);
}
@keyframes marquee {
to {
transform: translateX(-50%);
}
}
@keyframes caret-blink {
50% {
opacity: 0;
}
}
@keyframes status-pulse {
0% {
box-shadow: 0 0 0 0 color-mix(in oklab, currentColor 45%, transparent);
}
70%, 100% {
box-shadow: 0 0 0 6px transparent;
}
}
@keyframes orbit {
to {
transform: rotate(360deg);
}
}
@keyframes word-cycle {
0%, 3% {
transform: translateY(110%);
opacity: 0;
}
7%, 24% {
transform: translateY(0);
opacity: 1;
}
29%, 100% {
transform: translateY(-110%);
opacity: 0;
}
}
@property --beam-angle {
syntax: "<angle>";
initial-value: 0deg;
inherits: false;
}
@keyframes beam-rotate {
to {
--beam-angle: 360deg;
}
}
@keyframes flux-x {
from {
transform: translateX(-10rem);
}
to {
transform: translateX(82rem);
}
}
@keyframes flux-y {
from {
transform: translateY(-10rem);
}
to {
transform: translateY(56rem);
}
}