A curated collection of book recommendations from notable people. Browse books and recommenders, discover related content through semantic search, and explore connections between books and the people who recommend them.
Booklist aggregates book recommendations from various sources and presents them in an interactive, searchable interface. Key features include:
- Dual View Modes: Browse by books or by recommenders (people)
- Smart Search: Find books by title, author, genre, or semantic similarity
- Rich Metadata: See recommendation counts, percentile rankings, and background colors indicating popularity
- Related Content: Discover books recommended by the same people or semantically similar books
- Detailed Views: Click any book or recommender to see full details, recommendations, and connections
The application is built with Next.js and uses Supabase as its database:
-
Data Layer: A Supabase PostgreSQL database stores books, people (recommenders), and their recommendations
- Uses vector embeddings for semantic similarity search
- Calculates percentile rankings based on recommendation counts
-
Data Pipeline: The
dump-data.tsscript runs before each build to:- Fetch all books and recommenders from Supabase
- Calculate recommendation percentiles and bucket rankings (0-5)
- Generate static JSON files in
public/data/for fast client-side access - Split book data into essential fields for browsing and extended fields for detail views
-
Frontend: A React/Next.js app that:
- Server-renders the first visible rows and sends the complete compact catalog for the selected view
- Loads the other catalog through
/booklist/api/catalog/[view]only when switching views - Fetches one full book or person through
/booklist/api/entities/[id]when opening a detail panel - Sorts and filters locally; URL updates preserve sharing and browser history without refetching the page
- Keeps scrolling in a separate virtualized row component with memoized rows and fixed heights
- Uses SWR's immutable cache for catalogs and details during the session
This project is configured to be hosted at the /booklist path (e.g., https://example.com/booklist) rather than at the root. This is reflected in several places:
next.config.tshasbasePath: "/booklist"config/site.tshasurl: "https://basecase.vc/booklist"app/api/og/route.tsxreferences/booklist/fonts/...- Catalog and detail endpoints live under
/booklist/api/... - The recommendation wizard still loads static files from
/booklist/data/...
To run this project at the root path instead (e.g., https://example.com/):
-
Remove the basePath in
next.config.ts:- basePath: "/booklist", -
Update the site URL in
config/site.ts:- url: "https://basecase.vc/booklist", + url: "https://your-domain.com",
-
Update the font path in
app/api/og/route.tsx:- new URL('/booklist/fonts/SpecialElite-Regular.ttf', process.env.NEXT_PUBLIC_VERCEL_URL) + new URL('/fonts/SpecialElite-Regular.ttf', process.env.NEXT_PUBLIC_VERCEL_URL)
-
Update client fetch paths (remove the
/booklistprefix from API and static-data URLs):- "/booklist/data/books-essential.json" + "/data/books-essential.json"
- Node.js 20 or higher
- npm or your preferred package manager
- A Supabase project with the required schema
-
Clone the repository
git clone <your-repo-url> cd booklist
-
Install dependencies
npm install
-
Configure environment variables
Create a
.env.localfile in the project root:NEXT_PUBLIC_SUPABASE_URL=your_supabase_project_url NEXT_PUBLIC_SUPABASE_ANON_KEY=your_supabase_anon_key SUPABASE_SERVICE_ROLE_KEY=your_supabase_service_role_key OPENAI_API_KEY=your_openai_api_key NEXT_PUBLIC_VERCEL_URL=https://your-domain.com
Supabase credentials: Get these from your Supabase project settings at
https://app.supabase.com/project/_/settings/apiOPENAI_API_KEY: Required for semantic search functionality. The app uses OpenAI's
text-embedding-ada-002model to generate embeddings for similarity search. Get your API key from OpenAI's platform.NEXT_PUBLIC_VERCEL_URL: This is used for the OG image generation endpoint. Set it to your domain (e.g.,
https://example.comorhttp://localhost:3000for local development). When deploying to Vercel, this is set automatically. -
Set up the database
The required schema is in
supabase/migrations/20250605153046_initial.sql. Apply it to your Supabase project:# If using Supabase CLI (recommended): supabase db push # Or manually run the migration file in the Supabase SQL editor
The schema includes:
bookstable with vector embeddings for semantic searchpeopletable for recommendersrecommendationstable linking people to books- Several RPC functions for efficient data fetching
-
Populate your database
Add books, people, and recommendations to your Supabase database. The application expects:
- Books with title, author, description, genre, and optional embeddings
- People (recommenders) with full_name, type, url, and optional description
- Recommendations linking people to books with source information
-
Run the development server
npm run dev
This will:
- Run the
dump-datascript to fetch data from Supabase - Generate static JSON files in
public/data/ - Start the Next.js development server
- Run the
-
Open the app
Navigate to http://localhost:3000 in your browser
npm run build
npm startThe build process will regenerate all data files from Supabase before creating the production bundle.
/app- Next.js app router pages and layouts/components- React components (grids, detail views, UI elements)/scripts- Data processing scripts (dump-data.ts)/supabase- Database migrations and configuration/utils- Utility functions and Supabase clients/public/data- Generated JSON files (git-ignored, created at build time)
- Framework: Next.js 15 with React 19
- Database: Supabase (PostgreSQL with pgvector)
- Styling: Tailwind CSS
- State Management: SWR for data fetching
- UI Components: Radix UI primitives
- Virtualization: @tanstack/react-virtual for performant large lists
Run npx tsx --test utils/catalog.test.ts to verify the compact catalogs retain all names, titles, recommendation counts, and descriptions used by the grid while excluding detail-only fields.
Use npx next build to build against existing generated snapshots without refreshing data from Supabase. npm run build refreshes the snapshots first.
For browser regressions, run npx playwright install chromium once, start a production server with npm run start, and run npm run test:browsing. Set BASE_URL to use another port, or BROWSER_CHANNEL=chrome to use an installed Chrome instead. The suite checks server-rendered rows with JavaScript disabled, continuous-list coverage, detail navigation, filters, sorting, history, mobile sizing, lazy catalogs, and failed-load retries.