solaceui
blockbento-grid-2source: ok · refreshed
Bento Grid 2
Solace UI bento grid block variation 2.
npx shadcn add "https://www.solaceui.com/r/bento-grid-2.json"free
Where it goes
bento-grid
Visual style
bento
Tags auto-generated by rules on 9/24/2026 — empty means no evidence, not lack of support.
Dependencies
Bundle weight · mediumanimationmotion
iconslucide-react
Files2 · source cached 10/8/2026, 10:22:56 AM
src/registry/blocks/bento-grid/two/index.tsx
"use client";
import { useState } from "react";
import {
BentoGridTemplateTwo,
BentoItem,
} from "@/registry/blocks/bento-grid/two/bento-grid-template-two";
const sampleBentoData: BentoItem[] = [
{
id: "design-system",
title: "Visual Design System",
description: "A comprehensive blueprint system built for layout structure.",
size: "large",
tag: "Blueprint",
link: "#design",
},
{
id: "ai-studio",
title: "AI Studio",
description: "Train and run agent prompting nodes with simple inputs.",
size: "small",
tag: "Active",
link: "#ai",
},
{
id: "analytics",
title: "Analytics Engine",
description: "Real-time metric telemetry and sparkline graphing.",
size: "small",
tag: "Live",
link: "#analytics",
},
{
id: "cloud-platform",
title: "Cloud Delivery Edge",
description: "Globally distributed regional edge nodes with real-time status reporting.",
size: "wide",
tag: "Edge",
link: "#cloud",
},
{
id: "security",
title: "Security Suite",
description: "Enterprise access control with JWT signature validation.",
size: "small",
tag: "Secure",
link: "#security",
},
{
id: "dev-hub",
title: "Developer Hub",
description: "CLI components and automated workspace setups.",
size: "small",
tag: "CLI Tool",
link: "#docs",
},
{
id: "collaboration",
title: "Realtime Collaboration",
description: "Multiplayer live presence cursors and shared scaling viewports.",
size: "small",
tag: "Multiplayer",
link: "#collaboration",
},
{
id: "community",
title: "Community & Integrations",
description: "Sync schemas across third-party operations pipelines.",
size: "small",
tag: "Sync Ready",
link: "#community",
},
];
export default function BentoGridTwo() {
const [items] = useState<BentoItem[]>(sampleBentoData);
return (
<div className="container mx-auto px-4 py-16 md:py-24">
<BentoGridTemplateTwo items={items} gap={6} animate={true} />
</div>
);
}
src/registry/blocks/bento-grid/two/bento-grid-template-two.tsx
"use client";
import React, { useState, useEffect } from "react";
import { motion } from "motion/react";
import { cn } from "@/lib/utils";
import Link from "next/link";
import { ArrowUpRight, Lock, CheckCircle2, RefreshCw } from "lucide-react";
export type BentoItem = {
id: string;
title: string;
description?: string;
size?: "small" | "medium" | "large" | "wide" | "tall";
tag?: string;
link?: string;
};
export type BentoGridProps = {
items: BentoItem[];
className?: string;
animate?: boolean;
gap?: number;
};
export function BentoGridTemplateTwo({
items,
className,
animate = true,
gap = 6,
}: BentoGridProps) {
return (
<div
className={cn(
"grid grid-cols-1 md:grid-cols-4 auto-rows-[minmax(270px,auto)] w-full max-w-7xl mx-auto",
className
)}
style={{ gap: `${gap * 0.25}rem` }}
>
{items.map((item, i) => {
return (
<motion.div
key={item.id}
initial={animate ? { opacity: 0, y: 15 } : undefined}
animate={animate ? { opacity: 1, y: 0 } : undefined}
transition={{
duration: 0.4,
delay: animate ? i * 0.04 : 0,
ease: [0.25, 1, 0.5, 1],
}}
className={cn(
"col-span-1 h-full",
item.size === "large" && "md:col-span-2 md:row-span-2",
item.size === "wide" && "md:col-span-2 md:row-span-1"
)}
>
<BentoGridItem item={item} />
</motion.div>
);
})}
</div>
);
}
// Card 1: Visual Design System Morphing Mockup
function DesignSystemMockup() {
const [isMobile, setIsMobile] = useState(false);
useEffect(() => {
const interval = setInterval(() => {
setIsMobile((prev) => !prev);
}, 3000);
return () => clearInterval(interval);
}, []);
return (
<div className="w-full h-full flex flex-col justify-between border border-zinc-200 bg-zinc-50/50 dark:border-zinc-900 dark:bg-[#050507] p-5 relative overflow-hidden select-none min-h-[220px]">
{/* Blueprint Grid Lines */}
<div className="absolute inset-0 opacity-10 pointer-events-none">
<svg width="100%" height="100%" xmlns="http://www.w3.org/2000/svg" className="text-zinc-900 dark:text-white">
<defs>
<pattern id="design-grid" width="12" height="12" patternUnits="userSpaceOnUse">
<path d="M 12 0 L 0 0 0 12" fill="none" stroke="currentColor" strokeWidth="0.5" />
</pattern>
</defs>
<rect width="100%" height="100%" fill="url(#design-grid)" />
</svg>
</div>
{/* Header details */}
<div className="relative z-10 flex justify-between items-center font-mono text-[9px] text-zinc-400 dark:text-zinc-500 border-b border-zinc-200 dark:border-zinc-900 pb-2 mb-2">
<span className="text-zinc-700 dark:text-zinc-300 font-semibold tracking-wider">RESPONSIVE CANVAS</span>
<span>{isMobile ? "VIEWPORT: MOBILE" : "VIEWPORT: DESKTOP"}</span>
</div>
{/* Morphing layout boxes */}
<div className="relative z-10 flex-grow flex flex-col justify-center gap-4 py-2">
<motion.div
layout
transition={{ duration: 0.6, ease: [0.25, 1, 0.5, 1] }}
className={cn(
"w-full flex gap-3",
isMobile ? "flex-col items-stretch" : "flex-row items-center"
)}
>
{/* Box 1: Core Hero block */}
<motion.div
layout
className="flex-grow p-3 border border-zinc-200 bg-white/80 dark:border-zinc-800 dark:bg-zinc-950/60 min-h-[50px] flex flex-col justify-between rounded-none"
>
<div className="h-1.5 w-1/3 bg-zinc-300 dark:bg-zinc-850" />
<div className="h-1 w-full bg-zinc-200 dark:bg-zinc-900/60 mt-2" />
</motion.div>
{/* Box 2: Secondary card */}
<motion.div
layout
className={cn(
"p-3 border border-zinc-200 bg-white/80 dark:border-zinc-800 dark:bg-zinc-950/60 min-h-[50px] flex flex-col justify-between rounded-none",
isMobile ? "w-full" : "w-[30%]"
)}
>
<div className="h-1.5 w-1/2 bg-zinc-300 dark:bg-zinc-850" />
<div className="h-1 w-full bg-zinc-200 dark:bg-zinc-900/60 mt-2" />
</motion.div>
{/* Box 3: Tertiary card */}
<motion.div
layout
className={cn(
"p-3 border border-zinc-200 bg-white/80 dark:border-zinc-800 dark:bg-zinc-950/60 min-h-[50px] flex flex-col justify-between rounded-none",
isMobile ? "w-full" : "w-[25%]"
)}
>
<div className="h-1.5 w-2/3 bg-indigo-500/20 dark:bg-indigo-500/15" />
<div className="h-1 w-full bg-zinc-200 dark:bg-zinc-900/60 mt-2" />
</motion.div>
</motion.div>
{/* Canvas alignment indicators */}
<div className="flex justify-between items-center font-mono text-[8px] text-zinc-400 dark:text-zinc-650 mt-1">
<div className="flex items-center gap-1.5">
<span className="h-1 w-1 bg-indigo-500 rounded-full" />
<span>Auto-Layout: Flex</span>
</div>
<span>Gap: 12px</span>
</div>
</div>
{/* Footer details */}
<div className="relative z-10 text-[9px] font-mono text-zinc-400 dark:text-zinc-650 flex justify-between items-center pt-3 border-t border-zinc-200 dark:border-zinc-900">
<span>COORDINATE MAPPING ENGINE</span>
<span className="h-1.5 w-1.5 bg-indigo-500 animate-ping rounded-none" />
</div>
</div>
);
}
// Card 2: AI Studio typewriting effect
function AIStudioMockup() {
const text = "Design modular navigation bar with responsive layout";
const [displayText, setDisplayText] = useState("");
useEffect(() => {
let i = 0;
let isDeleting = false;
let timer: NodeJS.Timeout;
const type = () => {
if (!isDeleting) {
setDisplayText(text.slice(0, i));
i++;
if (i > text.length) {
isDeleting = true;
timer = setTimeout(type, 1500); // pause at the end
return;
}
timer = setTimeout(type, 70);
} else {
setDisplayText(text.slice(0, i));
i--;
if (i < 0) {
isDeleting = false;
i = 0;
timer = setTimeout(type, 500); // pause before starting over
return;
}
timer = setTimeout(type, 30);
}
};
type();
return () => clearTimeout(timer);
}, []);
return (
<div className="w-full h-[120px] flex flex-col justify-between border border-zinc-200 bg-zinc-50/50 dark:border-zinc-900 dark:bg-[#040405] p-3 font-mono text-[10px] text-zinc-500 relative overflow-hidden select-none">
<div className="space-y-1">
<div className="flex items-center justify-between text-zinc-650 dark:text-zinc-400 pb-1.5 border-b border-zinc-200 dark:border-zinc-900/60">
<span>Agent Prompt</span>
<span className="text-[9px] text-zinc-400 dark:text-zinc-600">Model: sol-v4</span>
</div>
<div className="pt-2 text-[10px] leading-relaxed text-zinc-750 dark:text-zinc-400 min-h-[40px]">
<span className="text-zinc-400 dark:text-zinc-600">> </span>
<span>{displayText}</span>
<span className="animate-pulse text-indigo-500 dark:text-indigo-400 font-bold ml-0.5">|</span>
</div>
</div>
<div className="flex justify-between items-center pt-2 border-t border-zinc-200 dark:border-zinc-900/60">
<span className="text-[9px] text-zinc-400 dark:text-zinc-600">Tokens: {displayText.length * 3} / 8k</span>
<button className="px-2.5 py-0.5 bg-white border border-zinc-200 hover:bg-zinc-50 text-zinc-800 dark:bg-zinc-900 dark:border-zinc-800 dark:text-white dark:hover:bg-zinc-800 transition-colors cursor-pointer text-[9px] rounded-none">
EXECUTE
</button>
</div>
</div>
);
}
// Card 3: Analytics build of curve animation
function AnalyticsMockup() {
return (
<div className="w-full h-[120px] flex flex-col justify-between border border-zinc-200 bg-zinc-50/50 dark:border-zinc-900 dark:bg-[#040405] p-3 font-mono text-[10px] text-zinc-500 relative overflow-hidden select-none">
<div className="flex items-center justify-between text-zinc-650 dark:text-zinc-400 pb-1.5 border-b border-zinc-200 dark:border-zinc-900/60">
<span>Telemetry Rate</span>
<span className="text-[9px] text-emerald-500 dark:text-emerald-400 flex items-center gap-1">
<span className="h-1.5 w-1.5 rounded-full bg-emerald-500 dark:bg-emerald-400 animate-pulse" />
ONLINE
</span>
</div>
<div className="py-2.5 flex items-center justify-center">
<svg className="w-full h-10" viewBox="0 0 100 30" fill="none" xmlns="http://www.w3.org/2000/svg">
{/* Background grid lines */}
<line x1="0" y1="10" x2="100" y2="10" stroke="currentColor" className="text-zinc-200/50 dark:text-zinc-900/50" strokeWidth="0.5" />
<line x1="0" y1="20" x2="100" y2="20" stroke="currentColor" className="text-zinc-200/50 dark:text-zinc-900/50" strokeWidth="0.5" />
<motion.path
d="M0 25 L15 20 L30 22 L45 8 L60 18 L75 10 L90 5 L100 8"
stroke="#6366f1"
strokeWidth="1.5"
fill="none"
animate={{ pathLength: [0, 1, 1, 0] }}
transition={{
duration: 5,
repeat: Infinity,
ease: "easeInOut",
times: [0, 0.7, 0.9, 1]
}}
/>
<motion.circle
cx="100"
cy="8"
r="2"
fill="#818cf8"
animate={{
opacity: [0, 0, 1, 0],
scale: [0.5, 1, 1.4, 0.5]
}}
transition={{
duration: 5,
repeat: Infinity,
ease: "easeInOut",
times: [0, 0.65, 0.7, 1]
}}
/>
</svg>
</div>
<div className="flex justify-between items-center text-[9px] text-zinc-400 dark:text-zinc-600">
<span>AVG LATENCY: 14ms</span>
<span>SYNC RATE: 99.98%</span>
</div>
</div>
);
}
// Card 4: Cloud Latency Monitor Mockup
function CloudMockup() {
const [latency1, setLatency1] = useState(11);
const [latency2, setLatency2] = useState(24);
const [latency3, setLatency3] = useState(38);
useEffect(() => {
const interval = setInterval(() => {
setLatency1(Math.floor(9 + Math.random() * 5));
setLatency2(Math.floor(21 + Math.random() * 6));
setLatency3(Math.floor(35 + Math.random() * 7));
}, 2000);
return () => clearInterval(interval);
}, []);
return (
<div className="w-full h-[100px] flex flex-col justify-between border border-zinc-200 bg-zinc-50/50 dark:border-zinc-900 dark:bg-[#040405] p-3 font-mono text-[10px] text-zinc-500 relative overflow-hidden select-none">
<div className="flex items-center justify-between text-zinc-600 dark:text-zinc-400 pb-1.5 border-b border-zinc-200 dark:border-zinc-900/60">
<span>Regional Edge Nodes</span>
<span>Active Distribution</span>
</div>
<div className="grid grid-cols-3 gap-2.5 py-1">
<div className="p-2 border border-zinc-205 bg-white dark:border-zinc-900 dark:bg-zinc-950/40 flex items-center justify-between">
<span>US-EAST</span>
<span className="text-emerald-500 dark:text-emerald-400 flex items-center gap-1 font-semibold">
<span className="size-1 rounded-full bg-emerald-500 dark:bg-emerald-400 animate-ping" />
{latency1}ms
</span>
</div>
<div className="p-2 border border-zinc-205 bg-white dark:border-zinc-900 dark:bg-zinc-950/40 flex items-center justify-between">
<span>EU-WEST</span>
<span className="text-emerald-500 dark:text-emerald-400 flex items-center gap-1 font-semibold">
<span className="size-1 rounded-full bg-emerald-500 dark:bg-emerald-400" />
{latency2}ms
</span>
</div>
<div className="p-2 border border-zinc-205 bg-white dark:border-zinc-900 dark:bg-zinc-950/40 flex items-center justify-between">
<span>AP-SOUTH</span>
<span className="text-emerald-500 dark:text-emerald-400 flex items-center gap-1 font-semibold">
<span className="size-1 rounded-full bg-emerald-500 dark:bg-emerald-400" />
{latency3}ms
</span>
</div>
</div>
</div>
);
}
// Card 5: Security Lock Mockup
function SecurityMockup() {
const [isValidating, setIsValidating] = useState(false);
useEffect(() => {
const interval = setInterval(() => {
setIsValidating(true);
setTimeout(() => setIsValidating(false), 900);
}, 4000);
return () => clearInterval(interval);
}, []);
return (
<div className="w-full h-[120px] flex flex-col justify-between border border-zinc-200 bg-zinc-50/50 dark:border-zinc-900 dark:bg-[#040405] p-3 font-mono text-[9px] text-zinc-500 relative overflow-hidden select-none">
<div className="flex items-center justify-between text-zinc-650 dark:text-zinc-400 pb-1.5 border-b border-zinc-200 dark:border-zinc-900/60">
<span>Access Control</span>
<Lock className={cn("h-3 w-3 transition-colors duration-300", isValidating ? "text-emerald-500" : "text-indigo-500 dark:text-indigo-400")} />
</div>
<div className="py-2.5 break-all leading-normal opacity-85">
<span className="text-indigo-550 dark:text-indigo-400 font-medium">eyJhbGciOiJIUzI1NiJ9.</span>
<span className="text-zinc-600 dark:text-zinc-500">eyJuYW1lIjoiU2FyYWgifQ.</span>
<span className="text-zinc-700 dark:text-zinc-700">signature-verification-key</span>
</div>
<div className="flex justify-between items-center text-[8px] font-semibold">
<span>ALGORITHM: HS256</span>
<span className={cn("transition-colors duration-300", isValidating ? "text-emerald-500" : "text-zinc-400 dark:text-zinc-600")}>
{isValidating ? "VERIFYING..." : "SIGNATURE VALIDATED"}
</span>
</div>
</div>
);
}
// Card 6: Developer CLI mockup
function DeveloperHubMockup() {
const [step, setStep] = useState(0);
useEffect(() => {
const interval = setInterval(() => {
setStep((prev) => (prev + 1) % 3);
}, 3000);
return () => clearInterval(interval);
}, []);
return (
<div className="w-full h-[120px] flex flex-col justify-between border border-zinc-200 bg-zinc-50/50 dark:border-zinc-900 dark:bg-[#040405] p-3 font-mono text-[10px] text-zinc-500 relative overflow-hidden select-none">
<div className="flex items-center gap-1 text-zinc-400 dark:text-zinc-750 pb-1.5 border-b border-zinc-200 dark:border-zinc-900/60">
<div className="size-1.5 bg-zinc-300 dark:bg-zinc-800" />
<div className="size-1.5 bg-zinc-300 dark:bg-zinc-800" />
<div className="size-1.5 bg-zinc-300 dark:bg-zinc-800" />
<span className="ml-auto text-[8px] text-zinc-400 dark:text-zinc-600">terminal</span>
</div>
<div className="py-2 space-y-1.5 min-h-[50px] flex flex-col justify-center">
{step >= 0 && (
<div>
<span className="text-zinc-400 dark:text-zinc-600">$ </span>
<span className="text-zinc-900 dark:text-white">npx solaceui add bento-grid</span>
</div>
)}
{step === 1 && (
<div className="text-zinc-400 flex items-center gap-1.5 text-[9px]">
<RefreshCw className="h-2.5 w-2.5 animate-spin" />
<span>installing components...</span>
</div>
)}
{step === 2 && (
<div className="text-emerald-600 dark:text-emerald-500 opacity-90 text-[9px] flex items-center gap-1">
<CheckCircle2 className="h-3 w-3" />
<span>Component successfully added</span>
</div>
)}
</div>
<div className="text-[8px] text-zinc-405 dark:text-zinc-700 border-t border-zinc-200 dark:border-zinc-900/40 pt-1.5">
solaceui-cli@1.0.8
</div>
</div>
);
}
// Card 8: Community Integrations Sync Grid Mockup
function CommunityMockup() {
const [activeNode, setActiveNode] = useState(0);
useEffect(() => {
const interval = setInterval(() => {
setActiveNode((prev) => (prev + 1) % 4);
}, 1500);
return () => clearInterval(interval);
}, []);
const nodes = ["GitHub", "Figma", "Slack", "Linear"];
return (
<div className="w-full h-[120px] flex flex-col justify-between border border-zinc-200 bg-zinc-50/50 dark:border-zinc-900 dark:bg-[#040405] p-3 font-mono text-[9px] text-zinc-500 relative overflow-hidden select-none">
<div className="flex items-center justify-between text-zinc-600 dark:text-zinc-400 pb-1.5 border-b border-zinc-200 dark:border-zinc-900/60">
<span>Schema Sync</span>
<span className="text-[8px] text-indigo-400 animate-pulse">SYNCING</span>
</div>
<div className="grid grid-cols-2 gap-1.5 py-2">
{nodes.map((node, index) => {
const isActive = activeNode === index;
return (
<div
key={node}
className={cn(
"p-1 border text-center transition-all duration-300 rounded-none cursor-pointer",
isActive
? "border-indigo-500 bg-indigo-500/5 text-indigo-600 dark:border-indigo-400 dark:bg-indigo-400/5 dark:text-indigo-400 font-semibold"
: "border-zinc-200 bg-white hover:text-black dark:border-zinc-900 dark:bg-zinc-950/40 dark:hover:text-white"
)}
>
{node}
</div>
);
})}
</div>
</div>
);
}
function BentoGridItem({ item }: { item: BentoItem }) {
const { id, title, description, link, tag } = item;
// Helper to render card visual mockup components
const renderVisualMockup = () => {
switch (id) {
case "design-system":
return <DesignSystemMockup />;
case "ai-studio":
return <AIStudioMockup />;
case "analytics":
return <AnalyticsMockup />;
case "cloud-platform":
return <CloudMockup />;
case "security":
return <SecurityMockup />;
case "dev-hub":
return <DeveloperHubMockup />;
case "collaboration":
return (
<div className="w-full h-[120px] border border-zinc-200 bg-zinc-50/50 dark:border-zinc-900 dark:bg-[#040405] relative overflow-hidden select-none">
<div className="absolute inset-0 opacity-[0.03] pointer-events-none">
<svg width="100%" height="100%" xmlns="http://www.w3.org/2000/svg" className="text-zinc-900 dark:text-white">
<defs>
<pattern id="collab-grid" width="10" height="10" patternUnits="userSpaceOnUse">
<circle cx="5" cy="5" r="0.5" fill="currentColor" />
</pattern>
</defs>
<rect width="100%" height="100%" fill="url(#collab-grid)" />
</svg>
</div>
{/* Cursor 1 */}
<motion.div
className="absolute left-[15%] top-[35%] flex items-center gap-1 pointer-events-none z-10"
animate={{ x: [0, 20, -5, 0], y: [0, -10, 15, 0] }}
transition={{ duration: 7, repeat: Infinity, ease: "easeInOut" }}
>
<svg width="10" height="10" viewBox="0 0 10 10" fill="none">
<path d="M0 0 L8 3 L5 5 L3 8 Z" fill="#6366f1" />
</svg>
<span className="px-1 py-0.2 bg-[#6366f1] text-white text-[7px] font-semibold uppercase tracking-wider rounded-none">SARAH</span>
</motion.div>
{/* Cursor 2 */}
<motion.div
className="absolute left-[55%] top-[20%] flex items-center gap-1 pointer-events-none z-10"
animate={{ x: [0, -25, 10, 0], y: [0, 20, -5, 0] }}
transition={{ duration: 9, repeat: Infinity, ease: "easeInOut", delay: 0.5 }}
>
<svg width="10" height="10" viewBox="0 0 10 10" fill="none">
<path d="M0 0 L8 3 L5 5 L3 8 Z" fill="#ec4899" />
</svg>
<span className="px-1 py-0.2 bg-[#ec4899] text-white text-[7px] font-semibold uppercase tracking-wider rounded-none">MARCUS</span>
</motion.div>
</div>
);
case "community":
return <CommunityMockup />;
default:
return null;
}
};
const ContentWrapper = (
<div className="group/item relative flex flex-col justify-between h-full bg-white border border-zinc-200 text-zinc-700 dark:bg-[#060608] dark:border-zinc-900 dark:text-zinc-300 p-6 relative overflow-hidden transition-all duration-300 hover:border-zinc-300 dark:hover:border-zinc-800 hover:scale-[1.01] hover:-translate-y-0.5 hover:shadow-[0_12px_25px_rgba(0,0,0,0.04)] dark:hover:shadow-[0_12px_25px_rgba(0,0,0,0.6)] select-none rounded-none">
{/* Visual Header containing Tag and Arrow */}
<div className="flex justify-between items-center mb-4 z-10">
{tag && (
<span className="text-[9px] font-bold tracking-widest text-zinc-500 border border-zinc-250 bg-zinc-50 dark:border-zinc-900 dark:bg-zinc-950/50 px-2 py-0.5 rounded-none uppercase">
{tag}
</span>
)}
{link && (
<ArrowUpRight className="h-4 w-4 text-zinc-400 group-hover/item:text-zinc-900 dark:text-zinc-600 dark:group-hover/item:text-white group-hover/item:translate-x-0.5 group-hover/item:-translate-y-0.5 transition-all duration-300" />
)}
</div>
{/* Custom Visual Graphic Content */}
<div className="my-2 flex-grow flex items-center justify-center z-10">
{renderVisualMockup()}
</div>
{/* Title & Description at the bottom */}
<div className="mt-4 space-y-1.5 z-10">
<h3 className="text-sm font-semibold tracking-tight text-zinc-900 dark:text-white group-hover/item:text-zinc-950 dark:group-hover/item:text-zinc-100 transition-colors">
{title}
</h3>
{description && (
<p className="text-xs text-zinc-500 dark:text-zinc-500 leading-relaxed max-w-sm">
{description}
</p>
)}
</div>
</div>
);
if (link) {
return (
<Link href={link} className="block h-full w-full outline-none focus:outline-none rounded-none">
{ContentWrapper}
</Link>
);
}
return ContentWrapper;
}