# Personal Portfolio

> Landing template

A shadcn/ui block from the [@pacekit](https://ui.pacekit.dev/?utm_source=shadcn.exchange) registry, listed on [shadcn.exchange](https://shadcn.exchange/r/pacekit/personal-portfolio-template).

- Page, with a live preview: https://shadcn.exchange/r/pacekit/personal-portfolio-template
- Screenshot: https://abzjhwgwpdpz7ua5.public.blob.vercel-storage.com/shots/pacekit/personal-portfolio-template-light-6739b91cb0728941.png
- Registry item: https://ui.pacekit.dev/r/personal-portfolio-template.json
- Listed: 2026-10-21, source last changed 2026-10-21

## Install

```bash
npx shadcn@latest add https://shadcn.exchange/r/pacekit/personal-portfolio-template.json
```

Run it in a project that has shadcn/ui set up. It adds the files below and installs what they depend on.

## Dependencies

- Registry items: `badge`, `separator`, `button`, `@canvas-ui/blaze-react`

## Files

### `components/templates/personal-portfolio/about.tsx`

```tsx
import { Badge } from "@/components/ui/badge";

const narrative = [
    "I began my journey at the intersection of design and code, finding joy in making interfaces that feel alive, intuitive, and responsive. Over the years, I have worked with teams of all sizes to build products from scratch, establishing patterns that balance performance with elegant design.",
    "Currently, I am deep-diving into the capabilities of modern design systems, exploring how local AI systems can augment developer tooling, and refining my front-end craft. I believe in software that is fast, highly legible, and accessible to everyone.",
];

const workbench = [
    {
        category: "Languages",
        items: ["TypeScript", "JavaScript", "Go", "SQL", "HTML/CSS"],
    },
    {
        category: "Frameworks & Libs",
        items: ["React", "Next.js", "Vite", "Node.js"],
    },
    {
        category: "Tools & Craft",
        items: ["Figma", "Git", "Docker", "PostgreSQL"],
    },
];

export function About() {
    return (
        <section id="about" className="flex w-full max-w-6xl flex-col gap-12 py-16 ps-4 pe-4 sm:ps-6 sm:pe-6">
            <div className="flex flex-col gap-2">
                <span className="text-muted-foreground text-xs font-medium tracking-wider uppercase">
                    About & Craft
                </span>
                <p className="text-xl font-medium tracking-tight sm:text-2xl">Behind the interface.</p>
            </div>

            <div className="grid w-full grid-cols-1 items-start gap-12 md:grid-cols-2">
                <div className="flex flex-col gap-6">
                    {narrative.map((paragraph, index) => (
                        <p
                            key={index}
                            className="text-muted-foreground text-sm leading-relaxed font-normal sm:text-base">
                            {paragraph}
                        </p>
                    ))}
                </div>

                <div className="flex flex-col gap-6">
                    <h3 className="text-muted-foreground text-xs font-medium tracking-wider uppercase">
                        The Workbench
                    </h3>
                    <div className="flex flex-col gap-6">
                        {workbench.map((group) => (
                            <div key={group.category} className="flex flex-col gap-2">
                                <span className="text-muted-foreground text-xs font-medium">{group.category}</span>
                                <div className="flex flex-wrap gap-1.5">
                                    {group.items.map((item) => (
                                        <Badge key={item} variant="secondary" className="rounded-md">
                                            {item}
                                        </Badge>
                                    ))}
                                </div>
                            </div>
                        ))}
                    </div>
                </div>
            </div>
        </section>
    );
}
```

### `components/templates/personal-portfolio/footer.tsx`

```tsx
import { useEffect, useState } from "react";

import { Check, Copy } from "lucide-react";

import { Separator } from "@/components/ui/separator";

const socialLinks = [
    {
        label: "GitHub",
        href: "https://github.com",
    },
    {
        label: "LinkedIn",
        href: "https://linkedin.com",
    },
    {
        label: "Twitter",
        href: "https://x.com",
    },
];

export function Footer() {
    const [copied, setCopied] = useState(false);
    const [timeString, setTimeString] = useState("");

    useEffect(() => {
        const updateTime = () => {
            const now = new Date();
            setTimeString(
                now.toLocaleTimeString("en-US", {
                    hour: "2-digit",
                    minute: "2-digit",
                    timeZoneName: "short",
                }),
            );
        };
        updateTime();
        const interval = setInterval(updateTime, 60000);
        return () => clearInterval(interval);
    }, []);

    const copyEmail = async () => {
        await navigator.clipboard.writeText("hello@example.com");
        setCopied(true);
        setTimeout(() => setCopied(false), 2000);
    };

    return (
        <section
            id="connect"
            className="flex w-full max-w-6xl flex-col gap-16 py-16 ps-4 pe-4 sm:ps-8 sm:pe-8 md:py-24">
            <div className="grid w-full grid-cols-1 items-start gap-12 md:grid-cols-2">
                <div className="flex flex-col gap-4">
                    <span className="text-muted-foreground text-xs font-medium tracking-wider uppercase">Connect</span>
                    <h2 className="text-3xl leading-none font-medium tracking-tight sm:text-5xl">
                        Let's build something together.
                    </h2>
                </div>

                <div className="flex flex-col items-start gap-8">
                    <div className="flex flex-col gap-2">
                        <span className="text-muted-foreground text-xs font-medium">Get in Touch</span>
                        <button
                            onClick={copyEmail}
                            className="group hover:text-primary flex cursor-pointer items-center gap-2 text-xl font-medium tracking-tight transition-colors">
                            hello@example.com
                            {copied ? (
                                <Check className="size-4 text-emerald-500" />
                            ) : (
                                <Copy className="text-muted-foreground size-4" />
                            )}
                        </button>
                    </div>

                    <div className="flex w-full flex-col gap-2">
                        <span className="text-muted-foreground text-xs font-medium">Elsewhere</span>
                        <div className="flex flex-col gap-3">
                            {socialLinks.map((link) => (
                                <a
                                    key={link.label}
                                    href={link.href}
                                    target="_blank"
                                    rel="noopener noreferrer"
                                    className="text-muted-foreground hover:text-primary flex cursor-pointer items-center gap-2 text-sm font-medium transition-colors">
                                    {link.label}
                                </a>
                            ))}
                        </div>
                    </div>
                </div>
            </div>

            <div className="flex w-full flex-col gap-6">
                <Separator />

                <div className="text-muted-foreground flex flex-col gap-4 text-xs font-normal sm:flex-row sm:items-center sm:justify-between">
                    <span>&copy; {new Date().getFullYear()} Developer.</span>
                    <div className="flex items-center gap-4">
                        <span>{timeString}</span>
                        <span>Built with React & Tailwind</span>
                    </div>
                </div>
            </div>
        </section>
    );
}
```

### `components/templates/personal-portfolio/hero.tsx`

```tsx
import { ArrowDown, ArrowUpRight } from "lucide-react";

import { Button } from "@/components/ui/button";

const navItems = [
    { label: "Work", href: "#work" },
    { label: "About", href: "#about" },
    { label: "Connect", href: "#connect" },
];

const metadataItems = [
    { label: "Role", value: "Design Engineer" },
    { label: "Location", value: "San Francisco, CA" },
    { label: "Focus", value: "Typography & Minimalism" },
];

export function Hero() {
    return (
        <header className="flex w-full max-w-6xl flex-col gap-12 py-8 ps-4 pe-4 sm:ps-6 sm:pe-6 md:py-16">
            <div className="flex w-full items-center justify-between">
                <a href="/" className="text-lg font-semibold">
                    Developer
                </a>
                <nav className="flex items-center gap-4">
                    <ul className="flex items-center gap-4">
                        {navItems.map((item) => (
                            <li key={item.label}>
                                <a
                                    href={item.href}
                                    className="text-muted-foreground hover:text-primary cursor-pointer text-sm font-medium transition-colors">
                                    {item.label}
                                </a>
                            </li>
                        ))}
                    </ul>
                </nav>
            </div>

            <div className="flex flex-col items-start gap-6">
                <div className="flex items-center gap-2 rounded-md border py-1 ps-2 pe-2.5">
                    <div className="size-1.5 rounded-sm bg-emerald-500" />
                    <span className="text-muted-foreground text-xs font-medium">Building PaceUI</span>
                </div>

                <div className="flex flex-col gap-3">
                    <h1 className="text-2xl leading-tight font-medium tracking-tight sm:text-4xl">
                        Design engineer crafting minimal interfaces.
                    </h1>
                    <p className="text-muted-foreground max-w-xl text-xs leading-relaxed font-normal sm:text-sm">
                        I specialize in building clean, editorial interfaces and typography-driven designs. Currently
                        working on open source design systems and frontend tools.
                    </p>
                </div>

                <div className="grid w-full max-w-xl grid-cols-3 gap-6 border-t border-b py-3">
                    {metadataItems.map((item) => (
                        <div key={item.label} className="flex flex-col gap-0.5">
                            <span className="text-muted-foreground text-xs">{item.label}</span>
                            <span className="text-sm font-medium">{item.value}</span>
                        </div>
                    ))}
                </div>

                <div className="flex items-center gap-3">
                    <Button
                        variant="outline"
                        size="sm"
                        className="cursor-pointer"
                        render={
                            <a href="#work" className="flex items-center gap-1.5">
                                See Selected Work
                                <ArrowDown className="size-3.5" />
                            </a>
                        }></Button>
                    <Button
                        variant="ghost"
                        size="sm"
                        className="cursor-pointer"
                        render={
                            <a target="_blank" rel="noopener noreferrer" className="flex items-center gap-1.5">
                                Resume
                                <ArrowUpRight className="size-3.5" />
                            </a>
                        }></Button>
                </div>
            </div>
        </header>
    );
}
```

### `components/templates/personal-portfolio/index.tsx`

```tsx
import Blaze from "@/components/canvasui/Blaze";
import { About } from "@/components/templates/personal-portfolio/about";
import { Footer } from "@/components/templates/personal-portfolio/footer";
import { Hero } from "@/components/templates/personal-portfolio/hero";
import { SelectedWork } from "@/components/templates/personal-portfolio/work";

export const PortfolioLanding = () => {
    return (
        <div className="bg-background text-foreground flex min-h-screen w-full flex-col items-center">
            <Hero />
            <SelectedWork />
            <About />
            <Blaze className="min-h-160 w-full" distortion={0.4}>
                <div className="flex justify-center">
                    <Footer />
                </div>
            </Blaze>
        </div>
    );
};
```

### `components/templates/personal-portfolio/work.tsx`

```tsx
import { ArrowUpRight } from "lucide-react";

import { Badge } from "@/components/ui/badge";

const projects = [
    {
        title: "PaceUI",
        year: "2026",
        description: "A premium library of minimalist components and templates optimized for developer workflows.",
        tags: ["React", "TypeScript", "Tailwind CSS"],
        demoUrl: "https://paceui.com",
        sourceUrl: "https://github.com",
    },
    {
        title: "LayerStudio",
        year: "2025",
        description: "An intuitive collaborative workspace for canvas-based design and vector design engineering.",
        tags: ["Next.js", "WebGL", "Tailwind CSS", "Zustand"],
        demoUrl: "https://example.com",
        sourceUrl: "https://github.com",
    },
    {
        title: "Krypton",
        year: "2025",
        description: "A fast, privacy-focused developer analytics engine designed for zero-config deployments.",
        tags: ["Go", "ClickHouse", "React", "Docker"],
        demoUrl: "https://example.com",
        sourceUrl: "https://github.com",
    },
];

export function SelectedWork() {
    return (
        <section id="work" className="flex w-full max-w-6xl flex-col gap-12 py-16 ps-4 pe-4 sm:ps-6 sm:pe-6">
            <div className="flex flex-col gap-2">
                <span className="text-muted-foreground text-xs font-medium tracking-wider uppercase">
                    Selected Work
                </span>
                <h2 className="text-xl font-medium tracking-tight sm:text-2xl">Projects built with craft & focus.</h2>
            </div>

            <div className="grid w-full grid-cols-1 gap-8 sm:gap-12 md:grid-cols-2">
                {projects.map((project) => (
                    <div key={project.title} className="group flex cursor-pointer flex-col gap-4">
                        <div className="bg-muted relative flex aspect-video w-full flex-col overflow-hidden rounded-md border transition-transform duration-300 group-hover:scale-[1.01]">
                            <div className="bg-card flex h-6 items-center gap-1 border-b ps-3">
                                <div className="bg-border size-1.5 rounded-xs" />
                                <div className="bg-border size-1.5 rounded-xs" />
                                <div className="bg-border size-1.5 rounded-xs" />
                            </div>
                            <div className="flex grow items-center justify-center p-6">
                                <div className="bg-card flex w-full max-w-xs flex-col gap-3 rounded-md border p-4">
                                    <div className="bg-muted h-3 w-16 rounded-xs" />
                                    <div className="bg-muted h-2 w-full rounded-xs" />
                                    <div className="bg-muted h-2 w-3/4 rounded-xs" />
                                    <div className="flex gap-1">
                                        <div className="bg-muted h-4 w-10 rounded-xs" />
                                        <div className="bg-muted h-4 w-10 rounded-xs" />
                                    </div>
                                </div>
                            </div>
                        </div>

                        <div className="flex flex-col gap-2">
                            <div className="flex items-center justify-between">
                                <h3 className="text-sm font-medium tracking-tight sm:text-base">{project.title}</h3>
                                <span className="text-muted-foreground text-xs font-medium">{project.year}</span>
                            </div>
                            <p className="text-muted-foreground text-xs leading-relaxed font-normal sm:text-sm">
                                {project.description}
                            </p>
                            <div className="flex flex-wrap gap-1.5">
                                {project.tags.map((tag) => (
                                    <Badge key={tag} variant="secondary" className="rounded-md">
                                        {tag}
                                    </Badge>
                                ))}
                            </div>
                            <div className="mt-2 flex items-center gap-4">
                                <a
                                    href={project.demoUrl}
                                    target="_blank"
                                    rel="noopener noreferrer"
                                    className="text-muted-foreground hover:text-primary flex cursor-pointer items-center gap-1 text-xs font-medium transition-colors">
                                    Live Demo
                                    <ArrowUpRight className="size-3" />
                                </a>
                                <a
                                    href={project.sourceUrl}
                                    target="_blank"
                                    rel="noopener noreferrer"
                                    className="text-muted-foreground hover:text-primary flex cursor-pointer items-center gap-1 text-xs font-medium transition-colors">
                                    Source
                                    <ArrowUpRight className="size-3" />
                                </a>
                            </div>
                        </div>
                    </div>
                ))}
            </div>
        </section>
    );
}
```
