Git
Run a Next.js Typecheck and Build on Every Pull Request
Add a GitHub Actions workflow that installs dependencies, typechecks, and builds a Next.js app before a branch is merged.
- GitHub Actions
- Next.js
- CI
- TypeScript
On this page
A pull request should fail in public when the project no longer typechecks or no longer builds. That check belongs in the repository, in a workflow file, so every branch runs the same steps on a clean machine. Your laptop already has node_modules, a warm cache, and a .env.local the CI runner will not have. The runner is the honest test.
Keep the first workflow boring. Check out the code, install from the lockfile, run the TypeScript compiler with no emit, then run the Next.js build. Add tests on the day you have tests. A workflow that tries to lint, test, scan, and deploy on the first afternoon usually fails for a reason nobody can see, and people start ignoring it.
Put the workflow in the repo
GitHub reads workflow files from .github/workflows. This one runs on pull requests and on pushes to main. The Node version should be the major version you deploy with. npm ci installs exactly the lockfile, which is what you want in CI. A plain npm install can quietly change versions.
name: check
on:
pull_request:
push:
branches: [main]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
cache: npm
- run: npm ci
- run: npx tsc --noEmit
- run: npm run build
Let the build see only public config
next build will execute server code that reads environment variables at build time if your pages do that. Give CI the variables that are required to compile, and none of the production secrets. A site URL used for canonical links can be a repository variable. A database password and a mail API key should stay in production. If a page fetches private data during the build, change that page to fetch on request, or the check cannot pass on a clean runner.
- Run npm run build locally with .env.local renamed aside. Any crash you see there is the crash CI will see.
- Commit package-lock.json. npm ci refuses to run without it.
- Set the workflow Node version to the major version in production.
- Open a pull request that imports a missing type and confirm the check goes red.
Require the check before merge
A green workflow that nobody is required to wait for is a suggestion. In the repository settings, add a branch rule for main that requires the check job. Name it the way it appears on the pull request, which is the job name under the workflow name. After that, merge stays disabled until build has passed on the latest commit.
Cache the npm directory, which setup-node does when cache is npm, and leave Next.js build cache alone until the workflow is slow enough to care. A five-minute check on a small blog is a fair price. If install time dominates, the lockfile and the cache are the levers. If the build dominates, look at how much work the static generation does, not at a second CI provider.
Read the first failing step
When the run fails, open the log of the earliest red step. A type error belongs to tsc. A missing module, a bad import, or a page that threw during static generation belongs to the build. Fix that error in a new commit on the same branch. The workflow runs again. Avoid rerunning a failed job without a code change when the log already names the file.
