Upload a CSV, get instant charts. Sign in with GitHub or Google, drop in a file (or pick one of three sample datasets), and the dashboard builds a bar chart, a trend line, and summary stats automatically - no manual column mapping.
Live demo: quickcsv.vercel.app
- Next.js 16 (App Router, TypeScript, Turbopack)
- Auth.js v5 - GitHub + Google OAuth, JWT sessions (no database)
- shadcn/ui (Base UI + Tailwind v4) and next-themes for light/dark mode
- Recharts for the charts
- Papa Parse for CSV parsing - entirely client-side, nothing is uploaded to a server
- Vitest + React Testing Library for unit/component tests, Playwright for E2E - see Testing
npm install
cp .env.example .env.local
npm run devOpen http://localhost:3000. The landing page and
/login work immediately; /dashboard requires a signed-in session (see
below).
-
Generate a session secret and put it in
.env.local:node -e "console.log(require('crypto').randomBytes(32).toString('base64url'))"→
AUTH_SECRET=... -
GitHub: create an OAuth App at github.com/settings/developers with callback URL
http://localhost:3000/api/auth/callback/github. Put the client ID/secret inAUTH_GITHUB_ID/AUTH_GITHUB_SECRET. GitHub only supports one callback URL per OAuth App - if you also deploy this, you'll want a second app for production (see Deployment). -
Google: create an OAuth client at console.cloud.google.com/apis/credentials with callback URL
http://localhost:3000/api/auth/callback/google. Put the client ID/secret inAUTH_GOOGLE_ID/AUTH_GOOGLE_SECRET. Google OAuth clients support multiple redirect URIs, so the same client works for both local dev and production - just add both URLs.
See .env.example for the full list of variables, including
NEXT_PUBLIC_APP_URL (optional locally, used to build canonical Open
Graph URLs in production).
Three layers, each covering what it's actually good at:
| Layer | Tool | What it covers |
|---|---|---|
| Unit | Vitest | Pure logic: CSV type inference, parsing, column-magnitude ranking, category/time-series bucketing (lib/csv/*), string formatting (lib/format.ts) |
| Component | Vitest + React Testing Library | CsvUploader file-validation behavior (type/size/empty-file rejection, the happy path) |
| End-to-end | Playwright | Real browser flows against a production build: landing page, theme toggle, the /dashboard → /login auth redirect, CSP-clean page load, carousel navigation, and the full GitHub OAuth redirect chain |
Vitest + React Testing Library over Jest because it's faster and has
native ESM/Turbopack compatibility with no extra config; Playwright over
Cypress because it's the current Microsoft-backed standard for
multi-browser E2E and integrates cleanly with Next.js's own webServer
pattern.
npm test # unit + component tests (Vitest)
npm run test:watch # same, in watch mode
npm run test:e2e # Playwright E2E - builds and starts a production
# server automatically, then runs against itThe GitHub OAuth E2E test needs real credentials in .env.local (see
OAuth setup) and is skipped automatically if they're not
present, so a fresh clone can still run the rest of the suite with zero
config.
Both suites run automatically on every push and pull request via GitHub Actions - see the badge at the top of this file.
A full security review - what was checked, what was found, and what was fixed (a middleware-only auth bypass, missing security headers, and patched CVEs in transitive dependencies) - is documented in SECURITY.md.
- Import the repo at vercel.com/new - Next.js is auto-detected, no config needed.
- Add environment variables (Production scope only - see
SECURITY.md for why Preview is deliberately excluded
for OAuth credentials):
AUTH_SECRET,AUTH_GITHUB_ID,AUTH_GITHUB_SECRET,AUTH_GOOGLE_ID,AUTH_GOOGLE_SECRET. - Deploy once to get your assigned
*.vercel.appURL, then addNEXT_PUBLIC_APP_URLset to that URL and redeploy. - Update your GitHub OAuth App and Google OAuth Client callback URLs to point at the deployed domain (see OAuth setup).
docker build -t csv-insights .
docker run -p 3000:3000 --env-file .env.local -e PORT=3000 csv-insightsThe Dockerfile is a three-stage build (deps → builder → runner)
using Next.js's output: "standalone" mode - only the files each route
actually needs are traced and copied in, node_modules isn't shipped,
and the container runs as a non-root user. standalone output is gated
behind a DOCKER_BUILD env var set by the Dockerfile itself, so it has
no effect on the Vercel build, which already has its own optimized
pipeline.
Same OAuth callback rules apply as any other deployment: point your
OAuth apps' callback URLs at wherever the container is actually
reachable, and set NEXT_PUBLIC_APP_URL accordingly.
app/
page.tsx # public landing page
login/ # OAuth sign-in
dashboard/ # protected - upload/sample picker, charts, preview table
api/auth/[...nextauth]/ # Auth.js route handler
components/
landing/ # landing page sections
dashboard/ # uploader, sample picker, preview table, shell
charts/ # recharts wrappers (bar, line, stat tile, tooltip)
ui/ # shadcn/ui primitives
auth/ # OAuth sign-in buttons
lib/
auth.ts # Auth.js config
csv/ # parsing + column-type inference + auto-insights (+ *.test.ts)
sample-datasets.ts # metadata for the 3 bundled sample CSVs
actions/ # server actions (sign in/out)
types/
csv.ts # shared CSV/insights types
e2e/ # Playwright end-to-end specs
proxy.ts # route protection (Next 16's renamed middleware)
public/sample-data/ # the 3 sample CSVs
Dockerfile # multi-stage build for self-hosting
SECURITY.md # security review notes
lib/csv/insights.ts picks columns without any user input:
- The numeric column with the largest total magnitude becomes the primary metric (so "revenue" wins over "quantity").
- The categorical column with the best top-8 coverage becomes the breakdown axis - this avoids picking either an overly blunt 2-value split or a high-cardinality column that dumps most rows into "Other".
- If a date column exists, it's plotted as a time series, bucketed by month once there are more than 60 distinct dates.
This logic is covered by unit tests in lib/csv/insights.test.ts - see
Testing.
MIT - see LICENSE.