The Problem
Turning a LinkedIn profile into a personal website is manual, repetitive work. Most people either copy-paste content into a template or pay for a site builder. This generator automates the conversion by extracting structured resume data from a PDF export and rendering it as a deployable Next.js site.
What This Does
self.so is a Next.js app that ingests a LinkedIn PDF export, extracts resume data via a Together.ai LLM, and publishes it to a public route at /[username]. The pipeline lives in app/api/resume/route.ts (PDF processing) and app/api/username/route.ts (site publication). The resume editor is split between read-only components (components/resume/) and editing components (components/resume/editing/) that manage work experience, education, and skills.
The project is a fork of Nutlope/self.so (3,067 stars). It uses Clerk for auth, Upstash Redis as the database (lib/server/redisActions.ts), S3 for PDF storage (app/api/s3-upload/route.ts), and the Vercel AI SDK with Together.ai for LLM calls. A components/ui/ folder holds 30+ shadcn-style primitives.
How It Is Wired
The import graph maps 124 internal modules with 55 edges and zero circular dependencies. The two most connected modules are components/resume/editing/EditResume (imports 8 modules) and components/resume/FullResume (imports 7 modules)—both are leaf components with no importers, meaning they're mounted directly from pages. components/ui/label is the most-depended-upon module (5 importers, 0 dependencies), and lib/server/redisActions is the main effect carrier (4 importers, 1 dependency).
Execution flow: app/(private)/upload/page.tsx → app/api/s3-upload/route.ts (writes to S3) → app/api/resume/route.ts (calls Together.ai, writes parsed data to Redis) → app/[username]/page.tsx (reads Redis, renders FullResume). The app/[username]/og/route.tsx generates Open Graph images. A preview path exists at app/(private)/preview/ for editing before publish.
The wiring is clean—no cycles, short dependency chains. The risk is in components/resume/editing/EditResume.tsx, which orchestrates all editing dialogs (add skill, education, work experience) and carries the widest blast radius for UI changes.
How To Use It
Setup (from README):
pnpm install
pnpm run dev
Configuration: Copy .example.env to .env and fill in keys for Together AI, Upstash Redis, AWS S3, Clerk, and Helicone. The README documents each provider.
Running tests: pnpm test:run, pnpm test:ui, or pnpm test.
Real-World Use
A user logs in via Clerk, uploads a LinkedIn PDF. The app stores it in S3, runs Llama Guard for safety, sends it to Qwen via Together.ai with JSON-mode structured outputs, and writes the parsed resume to Redis. The user edits sections in the preview page, then publishes—their data is live at self.so/username with a shareable Open Graph card.
Code Health & Issues
Static analysis found 7 issues (3 high, 4 medium) across 3 kinds:
- High – Deep nesting:
components/ui/date-range-picker.tsx,components/ui/monthpicker.tsx,components/ui/chart.tsxhit max indentation depth 6. Control flow is hard to follow; extract guard clauses. - High – Duplicated code: 163 repeated 6-line blocks across 22 files, including
app/(private)/layout.tsx,app/layout.tsx, andapp/(private)/preview/client.tsx. Extract shared helpers. - Medium – Oversized file:
components/ui/sidebar.tsxat 706 lines. Split by responsibility.
SDLC observations: tests exist (5 files), CI is configured (GitHub Actions), no Dockerfile, no committed secrets. 19 of 72 dependencies are behind major versions—react-dropzone is 6 majors behind (14.3.8 → 20.1.0), @types/node is 4 behind, and typescript is 2 behind (^5 → 7.0.2).
The Bottom Line
A functional, well-structured resume-to-site generator with a clean module graph and real CI. The main maintenance burden is the duplicated UI logic and the aging dependency tree. Worth using if you want a self-hosted LinkedIn-to-personal-site pipeline and can tolerate the LLM/Redis/S3 setup cost.