Skip to main content
JPN Web Design is a Next.js 16 portfolio with a Japanese aesthetic. The source is organized into four primary concern areas — routing (app/), UI (components/), static content (data/), and utilities (lib/, types/) — plus static assets in public/.

Directory Tree

japanese-portfolio/
├── app/                        # Next.js App Router
   ├── layout.tsx              # Root layout (fonts, theme, nav, AI drawer)
   ├── page.tsx                # Home page (Hero → Works → Skills → Experience)
   ├── globals.css             # CSS variables, Japanese patterns, global styles
   └── api/
       └── chat/
           └── route.ts        # POST /api/chat — Groq streaming endpoint
├── components/                 # React components
   ├── Hero.tsx                # Full-viewport hero section
   ├── Works.tsx               # Project card grid
   ├── Skills.tsx              # Skill category grid
   ├── Experience.tsx          # Education + work timeline
   ├── TimelineSection.tsx     # Reusable timeline column
   ├── Navigation.tsx          # Fixed top nav (Server Component)
   ├── NavLinks.tsx            # Active-section nav links (Client Component)
   ├── AIDrawer.tsx            # Slide-in AI chat drawer
   ├── AIDrawerLazy.tsx        # Lazy wrapper for AIDrawer
   ├── ThemeProvider.tsx       # next-themes provider wrapper
   ├── ThemeToggle.tsx         # Dark/light toggle button
   ├── About.tsx               # About section
   ├── Contact.tsx             # Contact form section
   ├── Footer.tsx              # Page footer with social links
   └── Shared/
       └── ProfileImage.tsx    # Reusable circular profile image
├── data/                       # Static content
   ├── experience.ts           # educations[] and experiences[] arrays
   ├── projects.ts             # projects[] array
   ├── skills.ts               # skills[] array
   ├── socials.ts              # socialLinks[] array
   └── portfolio-context.ts   # generatePortfolioContext() for AI system prompt
├── lib/                        # Utility functions
   ├── utils.ts                # cn() — clsx + tailwind-merge
   └── chat-utils.tsx          # formatMessageContent() — URL + bold formatting
├── public/                     # Static assets served at /
   └── images/
       ├── home/               # Hero section images
       ├── patterns/           # Japanese pattern tiles (AVIF)
       ├── projects/           # Project screenshot thumbnails (AVIF)
       └── socials/            # Social platform icons (AVIF)
└── types/
    └── index.ts                # TimelineItem, Project, Skill, SocialLink interfaces

Top-Level Directories

app/

Contains everything that belongs to the Next.js App Router. Files named layout.tsx and page.tsx are special Next.js conventions; any other file is a co-located module. The api/ subdirectory holds server-side Route Handlers.

components/

All React components live here. Components without a "use client" directive at the top run as React Server Components. Those that need browser APIs (scroll events, useTheme, useChat) are marked "use client". The Shared/ subdirectory holds components that are imported by multiple parents.

data/

Plain TypeScript modules that export typed arrays and functions. No runtime fetching — all content is bundled at build time. The only exception is portfolio-context.ts, whose output is consumed by the API route at request time.

lib/

Two small utility modules:
FileExportPurpose
utils.tscn()Merges Tailwind class names with clsx + tailwind-merge
chat-utils.tsxformatMessageContent()Converts raw AI message text to React nodes, rendering URLs as <a> links and **bold** as <strong>

public/

Static files served directly at the root URL path. All images are in AVIF format for optimal compression. Japanese pattern tiles (patterns/) are referenced by CSS background-image rules in globals.css.

types/

A single file (index.ts) that exports the four shared TypeScript interfaces used across data/ and components/. See the Data Layer page for the full interface definitions.

Next.js App Router Structure

The project uses the App Router introduced in Next.js 13. The key conventions are:
app/
  layout.tsx   → Wraps every route; renders once per navigation tree
  page.tsx     → The UI for the / route
  api/
    chat/
      route.ts → Handles HTTP methods for /api/chat
app/layout.tsx is the root layout. It:
  • Loads Noto_Sans_JP and Noto_Serif_JP from Google Fonts via next/font
  • Wraps the entire app in <ThemeProvider> (next-themes)
  • Renders <Navigation /> above the page content
  • Renders <Contact /> and <AIDrawerLazy /> below — making them available on every page without needing to include them in page.tsx
  • Includes <Analytics /> from @vercel/analytics
app/page.tsx is the home route. It renders four section components in order:
export default function Home() {
  return (
    <>
      <Hero />
      <Works />
      <Skills />
      <Experience />
    </>
  );
}

The Chat API Route

app/api/chat/route.ts exposes a single POST handler at /api/chat. How it works:
  1. Receives a JSON body with a messages array (AI SDK UIMessage[] format)
  2. Calls getPortfolioContext(), which caches the output of generatePortfolioContext() for 5 minutes to avoid rebuilding the string on every request
  3. Passes the cached context string as the system prompt to Groq’s llama-3.1-8b-instant model via the AI SDK’s streamText function
  4. Returns a streaming text response via result.toTextStreamResponse()
export async function POST(request: Request) {
  const { messages } = await request.json();
  const portfolioContext = getPortfolioContext();
  const result = streamText({
    model: groq("llama-3.1-8b-instant"),
    system: portfolioContext,
    messages: await convertToModelMessages(messages),
  });
  return result.toTextStreamResponse();
}
The maxDuration = 30 export sets the Vercel serverless function timeout to 30 seconds, which is required for streaming responses.

Data Flow

Content moves from static TypeScript files through components to the browser in a single direction:
data/experience.ts  ──►  Experience.tsx  ──►  TimelineSection.tsx
data/projects.ts    ──►  Works.tsx
data/skills.ts      ──►  Skills.tsx
data/socials.ts     ──►  (used by portfolio-context.ts)
                    ─────────────────────────────────────────
data/portfolio-context.ts  ──►  app/api/chat/route.ts  ──►  AIDrawer.tsx
All data files import their types from types/index.ts, ensuring that the shape of the data matches what each component expects.
Because all data files are static TypeScript modules with no async exports, Next.js can fully render the Works, Skills, and Experience sections as React Server Components — no client-side fetching or hydration is needed for content.

Components

Props, render output, and implementation notes for every component.

Data Layer

TypeScript interfaces and how each data file connects to components.

Build docs developers (and LLMs) love