AR Portfolio
atroui/ar-portfolioFreeBlock
model-viewer AR portfolio with editable CONTENT models.
Install it
npx shadcn@latest add https://atroui.com/r/ar-portfolio.jsonSource
1"use client";23import Script from "next/script";4import { createElement, useState } from "react";56import { FadeIn } from "@/components/ui/fade-in";7import { cn } from "@/lib/utils";89/** Edit CONTENT to swap demo models. */10const CONTENT = [11 {12 id: "og-tool",13 title: "OG Image Generator",14 description: "Preview how your social cards look in 3D space before sharing.",15 src: "https://modelviewer.dev/shared-assets/models/Astronaut.glb",16 poster: "/opengraph-image",17 },18 {19 id: "dashboard",20 title: "SaaS Dashboard MVP",21 description: "Interactive 3D preview of a shipped client dashboard layout.",22 src: "https://modelviewer.dev/shared-assets/models/NeilArmstrong.glb",23 },24];2526const chipBase =27 "border px-3 py-1.5 text-xs font-medium transition-colors";28const chipActive = "border-brand bg-brand/10 text-brand";29const chipIdle =30 "border-border-subtle text-muted-foreground hover:border-border hover:text-foreground";3132export function ArPortfolio() {33 const [active, setActive] = useState(CONTENT[0]!.id);34 const model = CONTENT.find((m) => m.id === active) ?? CONTENT[0]!;35 const [scriptReady, setScriptReady] = useState(false);3637 return (38 <>39 <Script40 type="module"41 src="https://ajax.googleapis.com/ajax/libs/model-viewer/4.0.0/model-viewer.min.js"42 onLoad={() => setScriptReady(true)}43 strategy="lazyOnload"44 />4546 <FadeIn>47 <p className="ms-stamp">AR portfolio preview</p>48 <p className="mt-3 max-w-lg text-sm text-muted-foreground">49 On supported devices, tap “View in AR” to place a project preview in your50 space. Demo models are placeholders - replace the hardcoded{" "}51 <code className="font-mono text-[11px] text-foreground">CONTENT</code>{" "}52 list for real client work.53 </p>5455 <div className="mt-4 flex flex-wrap gap-2">56 {CONTENT.map((m) => (57 <button58 key={m.id}59 type="button"60 onClick={() => setActive(m.id)}61 className={cn(62 chipBase,63 "rounded-full",64 active === m.id ? chipActive : chipIdle65 )}66 >67 {m.title}68 </button>69 ))}70 </div>7172 <div className="mt-6 overflow-hidden rounded-2xl border border-border-subtle bg-card/40">73 {scriptReady ? (74 createElement("model-viewer", {75// … truncated
What it pulls in
npm packages
Other registry items
Files it writes
More from atroui
All 82 items| Component | Registry | Kind | Access | Installs | Command |
|---|---|---|---|---|---|
| Analytics Provideranalytics-provider | atroui | Blocks | Free | 1 dep | |
| Before / Afterbefore-after-slider | atroui | Blocks | Free | no deps | |
| Calendly Embedcalendly-embed | atroui | Blocks | Free | 1 dep | |
| Changelogchangelog | atroui | Blocks | Free | no deps | |
| Command Menucommand-menu | atroui | Blocks | Free | 4 deps | |
| Contact Formcontact-form | atroui | Blocks | Free | 2 deps | |
| Contextual CTAcontextual-cta | atroui | Blocks | Free | 2 deps | |
| Count Upcount-up | atroui | Blocks | Free | no deps |
