Skip to main content

Tech stack overview

Wrkks is built with modern web technologies optimized for performance and developer experience:
{
  "dependencies": {
    "next": "16.1.6",
    "react": "19.2.3",
    "react-dom": "19.2.3",
    "@clerk/nextjs": "^6.37.1",
    "@supabase/ssr": "^0.8.0",
    "openai": "^6.17.0",
    "zustand": "^5.0.10",
    "@tanstack/react-query": "^5.90.20",
    "tailwindcss": "^4"
  }
}

Core technologies

TechnologyPurposeVersion
Next.jsReact framework with App Router16.1.6
ReactUI library19.2.3
TypeScriptType safety5.x
Tailwind CSSStyling4.x
ClerkAuthentication6.37.1
SupabaseDatabase & backendLatest
OpenRouterAI resume parsingVia OpenAI SDK
ZustandState management5.0.10

Project structure

The Wrkks codebase follows Next.js 16 App Router conventions:
~/workspace/source/
├── app/
│   ├── (main)/              # Main application routes
│   │   ├── layout.tsx       # Main layout wrapper
│   │   ├── page.tsx         # Home page
│   │   ├── upload/          # Resume upload page
│   │   └── website/         # Website builder page
│   ├── (user)/              # User profile routes
│   │   ├── [slug]/          # Dynamic user pages
│   │   └── layout.tsx
│   ├── api/                 # API route handlers
│   │   ├── parse-resume/    # PDF parsing endpoint
│   │   ├── extract-info/    # AI extraction endpoint
│   │   ├── user/            # User CRUD operations
│   │   └── user-image/      # Image upload
│   ├── layout.tsx           # Root layout
│   ├── robots.ts            # SEO robots.txt
│   └── sitemap.ts           # SEO sitemap
├── components/
│   ├── buttons/             # Action buttons
│   ├── resume/              # Resume-related components
│   ├── ui/                  # Base UI components
│   ├── Hero.tsx
│   ├── FileUpload.tsx
│   └── SyncUser.tsx
├── lib/
│   ├── supabase/            # Supabase client & actions
│   │   ├── client.ts        # Browser client
│   │   ├── server.ts        # Server client
│   │   ├── resume/          # Resume operations
│   │   └── user/            # User operations
│   ├── types.ts             # TypeScript types
│   └── utils.ts             # Utility functions
├── hooks/
│   ├── stores/              # Zustand stores
│   │   └── useResumeStore.ts
│   └── use-file-upload.ts   # File upload hook
├── providers/
│   ├── tanstack-provider.tsx
│   └── theme-provider.tsx
└── public/                  # Static assets

Application layers

1. Presentation layer (Components)

React components handle all UI rendering and user interactions. Key patterns:
  • Server Components by default (Next.js 16)
  • Client Components marked with "use client"
  • Shadcn/ui for base components
  • Motion library for animations

2. State management (Zustand)

Client-side state is managed with Zustand stores:
import { create } from "zustand";
import { persist } from "zustand/middleware";

type ResumeStore = {
  resume: Resume | null;
  websiteStyle: WebsiteStyle;
  setResume: (resume: Resume) => void;
  updatePersonalInfo: (info: Partial<Resume["personalInfo"]>) => void;
  // ... more actions
};

export const useResumeStore = create<ResumeStore>()(persist(
  (set) => ({
    resume: null,
    websiteStyle: "simple",
    setResume: (resume) => set({ resume }),
    // ... implementations
  }),
  { name: "resume-store" }
));
Benefits:
  • Persists to localStorage automatically
  • Type-safe with TypeScript
  • Minimal boilerplate
  • React Query integration for server state

3. Data layer (Supabase)

Database operations use Supabase with separate client/server instances:
import { createBrowserClient } from "@supabase/ssr";

export const createClient = () =>
  createBrowserClient(
    process.env.NEXT_PUBLIC_SUPABASE_URL!,
    process.env.NEXT_PUBLIC_SUPABASE_PUBLISHABLE_DEFAULT_KEY!
  );

4. Authentication (Clerk)

Clerk handles all authentication flows:
import { ClerkProvider } from "@clerk/nextjs";

export default function RootLayout({ children }) {
  return (
    <ClerkProvider>
      <html lang="en">
        <body>{children}</body>
      </html>
    </ClerkProvider>
  );
}

Data flow

1

User uploads resume

User uploads a PDF file through the FileUpload component. The file is validated and stored in component state using the useFileUpload hook.
2

PDF parsing

File is sent to /api/parse-resume which uses pdf-parse-new to extract text content from the PDF.
3

AI extraction

Raw text is sent to /api/extract-info which calls OpenRouter AI to structure the resume data into JSON format.
4

State update

Structured resume data is stored in Zustand store (useResumeStore) and synced to localStorage.
5

Database save

Resume data is saved to Supabase users table via /api/user/update endpoint.
6

Website generation

User navigates to /website page where ResumePreview component renders the personal website using the stored resume data.

Type system

All data structures are strongly typed:
export interface Resume {
  personalInfo: PersonalInfo;
  summary: string;
  skills: Skills;
  experience: Experience[];
  projects: Project[];
  education: Education[];
  extracurricular: string[];
  customSections: CustomSection[];
}

export interface PersonalInfo {
  name: string;
  title: string;
  location: string;
  phone: string;
  email: string;
  website: string;
  linkedin: string;
  github: string;
  twitter: string;
  imageUrl: string;
}

export interface Experience {
  company: string;
  position: string;
  location: string;
  startDate: string;
  endDate: string;
  isCurrentRole: boolean;
  description: string[];
  technologies: string[];
}

Routing strategy

Route groups

Wrkks uses Next.js route groups for organization:
  • (main) - Main application routes (home, upload, website)
  • (user) - Public user profile pages with dynamic [slug] routing

Dynamic routes

app/(user)/[slug]/page.tsx
export default async function UserProfile({ params }) {
  const { slug } = params;
  // Fetch user by username slug
  const user = await getUserByUsername(slug);
  return <UserWebsite user={user} />;
}

Configuration

Next.js config

const nextConfig: NextConfig = {
  images: {
    remotePatterns: [
      { protocol: "https", hostname: "img.clerk.com" }
    ],
  },
  serverExternalPackages: ["pdf-parse-new"],
};

TypeScript config

Path aliases simplify imports:
tsconfig.json
{
  "compilerOptions": {
    "paths": {
      "@/*": ["./*"]
    }
  }
}
Use @/ prefix for absolute imports:
import { Resume } from "@/lib/types";
import { useResumeStore } from "@/hooks/stores/useResumeStore";

Performance optimizations

  • Server Components - Most components render on the server
  • React 19 - Automatic batching and transitions
  • Tailwind CSS 4 - JIT compilation for minimal CSS
  • Image optimization - Next.js Image component with remote patterns
  • Zustand persistence - Resume data cached in localStorage
  • Route prefetching - Automatic for Link components

Security considerations

  • Environment variables never exposed to client
  • Clerk handles authentication tokens
  • Supabase Row Level Security (RLS) policies
  • Server-side validation for all mutations
  • HTTPS-only in production

Build docs developers (and LLMs) love