Job list supports URL parameter filtering by department, location, employment type, remote, etc., with pagination
nextjs-job-board — Next.js Business Template
By startupkit-app
A Next.js job board template based on the Kit public jobs API, featuring job listings, detail pages, dynamic application forms, and talent pool registration, deployable to Vercel with one click.
Explore the design
Compare the page layouts and mobile experience. Open any screenshot for a closer look.
Actual screenshot captured by the server browser.
All pages & views 2
Screenshots show visual appearance; interactive behavior may differ.
What stands out
A quick read of the recorded project description.
Job detail pages include salary range, hiring process timeline, one-time milestone payment labeling, and schema.org JobPosting JSON-LD
Application form dynamically rendered from the API schema, covering 8 field types and 3 question types, with inline validation and accessibility support
Direct resume upload to storage: browser computes MD5, Server Action presigns, then the file is PUT directly to storage without passing through the Next.js server
Optional Cloudflare Turnstile anti-spam submission and Webhook cache invalidation; these do not take effect when keys are not configured
Optional Kit job analytics, reporting page views, application funnel, and other events via a browser-side tracker
Template Details
This template is a Next.js application for corporate hiring scenarios, with data sourced from the public jobs API of Kit (startupkit.app). The repository uses the MIT license, and the README explains that it can be deployed to Vercel with one click, or cloned locally and run after configuring environment variables.
Functionally, the template provides a job list with URL-based filtering (department, location, employment type, remote) and pagination, job detail pages (including salary range, hiring process timeline, schema.org JobPosting JSON-LD), and an application form dynamically rendered from the API schema, covering field types such as text, textarea, file, URL, select, checkbox, email, and phone, as well as text, scenario, and single-choice question types, with inline validation and accessibility support. Resume uploads use direct browser-to-storage transfer, presigned by a Server Action, avoiding large files passing through the Next.js server. There is also a /talent-pool talent pool registration form, which uses a consent-then-submit flow and supports double confirmation.
Optional capabilities include: enabling Kit job analytics via a publishable key (page views, unique visitors, sources, UTM, country, device, landing page, and the browse-to-apply funnel), enabling Cloudflare Turnstile anti-spam submission via a site key, and triggering tag-based cache invalidation via Webhook so newly published jobs are immediately visible. When the corresponding keys are not configured, these features do not take effect.
Data freshness depends on Next.js data caching: the job list revalidates every 60 seconds, job details and application forms revalidate every 300 seconds, and the talent pool form revalidates every 300 seconds, all cached by tag; Webhook can further refresh immediately. The README also notes that Webhook does not cover job edits and draft rollbacks.
Usage limitations: you need to obtain Kit keys (sk_…) yourself and configure environment variables; analytics must use a publishable key (pk_…), and if the key has an origin whitelist, the site domain must be in the whitelist; the template depends on the @startupkit-app/jobs package, and the README states its version is pinned to ^0.5.0.
Is it right for your project?
Use Cases
- Build a hiring page on an independent domain for a company, with job data hosted on Kit and displayed externally
- Recruitment sites that need dynamic application forms (including resume upload, custom questions, and spam submission protection)
- Teams that want to connect job pages with the Kit hiring portal, retain campaign attribution, and unify the application entry point
- Need a talent pool registration form to collect candidates interested in future opportunities but not current roles
- Want to use Webhook so newly published or closed jobs take effect on the site immediately
Suitable For
- Teams using Kit as a hiring data source and wanting to build their own frontend display
- Developers familiar with Next.js App Router and Vercel deployment
- Recruitment sites needing SEO-friendly job pages and structured data (Google for Jobs)
- Projects that accept configuring external keys via environment variables and independently verify third-party asset licenses
Getting started & customization
Use these recorded setup notes alongside the repository instructions for your chosen version.
- 1
Create keys in Kit under Hiring → Career Portal → Public API Keys; deployment requires STARTUPKIT_SECRET_KEY (sk_…)
- 2
Optional environment variables include STARTUPKIT_BASE_URL, NEXT_PUBLIC_STARTUPKIT_PUBLISHABLE_KEY, NEXT_PUBLIC_TURNSTILE_SITE_KEY, KIT_WEBHOOK_SIGNING_SECRET, NEXT_PUBLIC_COMPANY_NAME
- 3
Local run: clone the repository, run npm install, copy .env.example to .env.local and fill in the keys, then run npm run dev
- 4
Enabling analytics requires a publishable key (pk_…); if the key has an origin whitelist, the site domain must be in the whitelist
- 5
The Webhook endpoint points to POST /api/revalidate and requires a signing secret; the README states that job edits and draft rollbacks do not trigger events
License & source material
Usage conditions & limitations
Free to use · keep license noticeThis entry covers the free version under the MIT license in the public repository; the free scope is the code of this version and does not mean external services or third-party assets are free.
Must comply with when using
- Free use, modification, commercial use, and distribution of this version's code are permitted.
- When distributing copies or substantial portions of the code, retain the original author's copyright notice and the full MIT license notice.
- The code is provided as is; warranties and liability limitations are governed by the original license and applicable law.
Additional costs & items to verify
- Demo images, fonts, icons, and other third-party assets must be checked against their respective licenses; they cannot be judged solely by the code's MIT license.
- External services such as domains, hosting, databases, payments, or AI may incur additional charges.
- Before use, you must create and obtain keys (sk_…) on the Kit platform; the template itself does not provide job data and depends on the external Kit service.
- STARTUPKIT_SECRET_KEY is a required environment variable; without it, the application cannot run.
- Enabling analytics requires additionally configuring a publishable key (pk_…), and only publishable keys are valid.
- If the key has an origin whitelist, the site domain must be added to the whitelist; otherwise, analytics event reporting will return 403.
- The @startupkit-app/jobs dependency is pinned to ^0.5.0; upgrading requires manual modification.
- The one-time milestone payment field requires @startupkit-app/jobs 0.4 or newer to parse.
- Webhook does not cover job edits and draft rollbacks; these two types of changes will not refresh immediately and remain subject to the ISR cache window.
View Original Text Basis
Permission is hereby granted, free of charge
Original License Text
In Kit, go to **Hiring → Career Portal → Public API Keys** and create a key. You need the **secret key** (`sk_…`).
Original README
`STARTUPKIT_SECRET_KEY` | **Yes** | Secret API key (`sk_…`) from Kit → Hiring → Career Portal → Public API Keys
Original README
To turn it on, set `NEXT_PUBLIC_STARTUPKIT_PUBLISHABLE_KEY` to a **publishable** key (`pk_…`) from Kit → Hiring → Career Portal → Public API Keys. Only a publishable key works
Original README
If the key has an origin allowlist, your site's origin (e.g. `https://careers.example.com`) must be on it, or every batch is a 403 `origin_not_allowed`.
Original README
The dependency is pinned as `^0.5.0`.
Original README
This template consumes that field through `@startupkit-app/jobs` 0.4 or newer.
Original README
https://github.com/startupkit-app/nextjs-job-board/blob/main/LICENSEhttps://github.com/startupkit-app/nextjs-job-board/blob/main/README.mdKit emits no `job_posting.updated` event, so edits to a job's title or description are not pushed; and reverting a job to draft fires no event at all even though it removes the job from the API.
Original README
Compiled by DeepSeek based on repository license and README ·2026-10-06. Results only cover the materials read; dependencies, images, fonts, and subsequent term changes are subject to their respective original texts.
Project checks & description sources
Template Information
- Source platforms
- GitHub
- Documentation and maintenance reference score
- More documented · 80 / 100
- Technologies
- Next.js · React
- Website type
- Corporate Website
How this score is calculated
Our heuristic uses license records (up to 30), a demo link (12), a preview (10), stars (15), recorded maintenance (15), description completeness (8), feature records (5) and classification data (5). It is not a code, security or performance test, or a Google score. A link does not establish tested functionality.
What to verify for this project
- Repository update recorded
- 2026-10-06
- Screenshot last checked
- 2026-10-06
A demo link is listed. Check it with your own content and viewport sizes; its presence does not confirm that forms, authentication or payments work.
The catalog identifies Next.js as the primary stack. Check the actual dependency manifest and lockfile for versions and hosting requirements. These notes use recorded metadata, not a code audit or a deployment test.
Use a selection guide →How this description was prepared
AI-assisted description based on catalog records and repository material. Repository authors are not automatically the reviewers of this description.
Review declaration: [email protected] · 2026-10-06
Inspect the recorded source material
SHA-256: 47a73ed9f7d0008f4665a1c30f2d29d7034c8247fda473d44f8d2b31d7fb54fa
2026-10-06T10:23:37.541Z
Source file / repository# Kit Job Board — Next.js Template
A production-ready careers site built on the [Kit](https://startupkit.app) public hiring API.
Fork it, set one environment variable, and you have a fast, SEO-friendly job board with a
fully dynamic application form — resume uploads, custom questions, Turnstile spam protection,
and instant cache busting via webhooks.
**▶ Live demo: [nextjs-job-board-orcin.vercel.app](https://nextjs-job-board-orcin.vercel.app)** — this template deployed against real Kit job data.
[](https://vercel.com/new/clone?repository-url=https://github.com/startupkit-app/nextjs-job-board&env=STARTUPKIT_SECRET_KEY&envDescription=Secret%20API%20key%20from%20Kit%20Settings&envLink=https://startupkit.app/docs/public-jobs-api&demo-title=Kit%20Job%20Board&demo-description=A%20careers%20site%20on%20the%20Kit%20public%20hiring%20API&demo-url=https://nextjs-job-board-orcin.vercel.app)
## Features
- **Next.js App Router** (v16) — Server Components, Server Actions, ISR with tag-based revalidation
- **Job list** with URL-driven filters (department, location, employment type, remote) and pagination
- **Job detail pages** with salary ranges, hiring-process timeline, clearly labelled one-time
stage payments, and schema.org `JobPosting` JSON-LD (Google for Jobs ready), statically
generated via `generateStaticParams` + ISR
- **Application form rendered from the API schema** — all 8 field types (text, textarea, file,
url, select, checkbox, email, phone) and all 3 question types (text, scenario, multiple choice),
with inline validation errors, character counters, and full keyboard/screen-reader accessibility
- **Direct-to-storage resume uploads** — the browser computes an MD5 checksum, a Server Action
presigns the upload with your secret key, and the file PUTs straight to storage. Large files
never touch your Next.js server (no Vercel 4.5 MB body-limit issues), with a real progress bar
- **Talent-pool signup** at `/talent-pool` — a consent-first form for people who like the company
but not today's openings. The consent checkbox starts unchecked and its disclosure is rendered
from the API, the visitor's real IP is forwarded so the consent receipt records the person and
not your server, and Kit confirms the address by double opt-in. Adapting this into your own
codebase: hand your coding agent [TALENT_POOL.md](TALENT_POOL.md)
- **Optional Kit job analytics** — page views, unique visitors, traffic sources, UTM campaigns,
countries, devices, landing pages and the view → apply funnel in Kit → Hiring → Analytics,
reported from the visitor's browser by the SDK's tracker. Set a publishable key and it is on;
leave it unset and it is a no-op. Adding it to a site forked from an older version of this
template: hand your coding agent [ANALYTICS.md](ANALYTICS.md)
- **Optional Cloudflare Turnstile** spam protection (progressive enhancement — just set a site key)
- **Optional webhook revalidation** endpoint so newly published jobs appear instantly
- **Tailwind CSS v4**, dark mode, responsive, zero UI-kit dependencies — maximally forkable
## Quick start
### 1. Get an API key
In Kit, go to **Hiring → Career Portal → Public API Keys** and create a key. You need the
**secret key** (`sk_…`). It is only ever used server-side (Server Components and Server
Actions import it through a [`server-only`](https://www.npmjs.com/package/server-only) module),
so it never reaches the browser.
### 2. Deploy
Click the **Deploy** button above and paste your secret key when Vercel asks for
`STARTUPKIT_SECRET_KEY`. Done.
### Or run locally
```bash
git clone https://github.com/startupkit-app/nextjs-job-board
cd nextjs-job-board
npm install
cp .env.example .env.local # then paste your sk_… key
npm run dev
```
## Environment variables
| Variable | Required | Description |
| -------------------------------- | -------- | --------------------------------------------------------------------------- |
| `STARTUPKIT_SECRET_KEY` | **Yes** | Secret API key (`sk_…`) from Kit → Hiring → Career Portal → Public API Keys |
| `STARTUPKIT_BASE_URL` | No | API base URL. Defaults to `https://app.startupkit.app` |
| `NEXT_PUBLIC_STARTUPKIT_PUBLISHABLE_KEY` | No | Publishable key (`pk_…`) for browser-side job analytics. Unset = analytics off |
| `NEXT_PUBLIC_STARTUPKIT_BASE_URL` | No | Browser-side counterpart of `STARTUPKIT_BASE_URL`, for a custom Kit domain only |
| `NEXT_PUBLIC_TURNSTILE_SITE_KEY` | No | Cloudflare Turnstile site key — renders the widget on the apply form |
| `KIT_WEBHOOK_SIGNING_SECRET` | No | Signing secret of the Kit webhook endpoint targeting `POST /api/revalidate` |
| `NEXT_PUBLIC_COMPANY_NAME` | No | Company name for the header, titles, and JobPosting structured data |
## Connect your external careers site to Kit
Keep job descriptions at `/jobs/:public_token`, using `job.id` from the public API.
In Kit's Career Portal settings, set this deployment as your external careers homepage and
enable external job pages after verifying a live job URL. Public Kit job links then
send visitors to the matching page on your site and retain campaign attribution.
Explicit `destination=kit` links and Kit's dedicated application URLs stay on Kit.
See [CAREERS_URLS.md](CAREERS_URLS.md) for the fixed route contract, nested deployments,
application behavior and verification steps.
## How freshness works (ISR + webhooks)
All API reads go through Next.js' data cache:
- Job list: `revalidate: 60`, tagged `jobs`
- Job detail / apply form: `revalidate: 300`, tagged `jobs` and `job-<token>`
- Talent-pool form: `revalidate: 300`, tagged `talent-pool`
So the site is never more than a minute or two stale, with zero configuration. For **instant**
updates, create a webhook endpoint in Kit (Integrations → Webhooks) pointing at:
```
POST https://your-site.example/api/revalidate
```
and set `KIT_WEBHOOK_SIGNING_SECRET` to that endpoint's signing secret.
Kit authenticates deliveries by **signature**, not by a shared bearer token — it sends
`X-Webhook-Signature` (HMAC-SHA256 of `<timestamp>.<body>`, hex) alongside `X-Webhook-Timestamp`
and `X-Webhook-Event`, and cannot attach a custom `Authorization` header. The route recomputes
the HMAC over the raw request body and rejects anything that does not match, or whose timestamp
is more than 5 minutes old.
Subscribe to the job lifecycle events:
- `job_posting.published` / `job_posting.reopened` — the role becomes visible on the public API
- `job_posting.paused` / `job_posting.closed` — the role starts returning 404
All four bust both the `jobs` list tag and that job's `job-<token>` tag. Pausing and closing
matter as much as publishing: without them a cached page keeps advertising a role that is no
longer open. `application.*` events refresh just the job they belong to, which is useful if
roles auto-close at an application cap.
Two things webhooks do **not** cover, so the ISR window still earns its keep: Kit emits no
`job_posting.updated` event, so edits to a job's title or description are not pushed; and
reverting a job to draft fires no event at all even though it removes the job from the API.
## Analytics
Kit's **Hiring → Analytics** dashboard is fed by page events. Kit's hosted career portal sends
them itself; a headless site has to, or the dashboard stays empty. Pages here are ISR-cached,
so the events are sent from the visitor's browser by the SDK's `createTracker`
([`lib/kit-tracker.ts`](lib/kit-tracker.ts)), and Kit records them as the same events the
hosted portal writes, so every chart fills in unchanged:
| Event | Sent from |
| ----------------------- | ------------------------------------------------------------------- |
| `job_board.viewed` | `<TrackJobBoardView />` on the job list |
| `job.viewed` | `<TrackJobView job={…} />` on the job detail page |
| `application.started` | first keystroke, or focus other than autofocus, in the apply form |
| `application.submitted` | the apply form's success state |
| `talent_pool.joined` | the talent-pool form's success state |
To turn it on, set `NEXT_PUBLIC_STARTUPKIT_PUBLISHABLE_KEY` to a **publishable** key (`pk_…`)
from Kit → Hiring → Career Portal → Public API Keys. Only a publishable key works: visitor
identity (masked IP + user agent) and geo are read from the browser's own request, so a
server-side relay would count every visitor as your server, and the tracker refuses an
`sk_…` key outright. If the key has an origin allowlist, your site's origin (e.g.
`https://careers.example.com`) must be on it, or every batch is a 403 `origin_not_allowed`.
Verify: open a job page, and DevTools → Network shows `POST /api/public/v1/events` → `202`
within a second; Kit → Hiring → Analytics shows the view shortly after. Without the key
nothing is sent and nothing logs. In development, tracker errors (bad key, missing origin,
a Kit server without the endpoint yet) are `console.warn`ed; in production they are silent,
and the page is never affected either way.
## About the SDK
This template talks to Kit through the published **[`@startupkit-app/jobs`](https://www.npmjs.com/package/@startupkit-app/jobs)**
package. [`lib/kit.ts`](lib/kit.ts) is the single place that imports it: it re-exports the types
and exposes a `kit` client authenticated with the secret key. That module is marked `server-only`,
so the key can never be bundled into a client component.
[`lib/kit-tracker.ts`](lib/kit-tracker.ts) is the one other import: the browser-side analytics
tracker, which only ever sees the publishable key.
The dependency is pinned as `^0.5.0`. A caret on a `0.x` version does not cross minor releases,
so picking up a new SDK minor is always a deliberate edit here rather than something `npm install`
does on its own — worth knowing when the API grows a field the template wants to read.
## API surface used
Base URL `https://app.startupkit.app`, auth via `Authorization: Bearer sk_…`:
| Endpoint | Used for |
| ---------------------------------------------- | --------------------------------------------------------- |
| `GET /api/public/v1/jobs` | Job list, filters, pagination, `generateStaticParams` |
| `GET /api/public/v1/jobs/:public_token` | Job detail, application form schema, JSON-LD |
| `POST /api/public/v1/direct_uploads` | Presigning resume/file uploads (server action) |
| `POST /api/public/v1/jobs/:token/applications` | Submitting applications (server action) |
| `GET /api/public/v1/talent_pool` | Talent-pool form schema: fields, consent terms, resume |
| `POST /api/public/v1/talent_pool/entries` | Talent-pool signups (server action) |
| `POST /api/public/v1/events` | Job analytics events, from the browser with the `pk_…` key |
Error responses (`{ "error": { "code", "message", "fields" } }`) are surfaced as inline form
errors; `already_applied` (409), `already_in_talent_pool` (409), `consent_required` (422) and
`turnstile_failed` (422) get friendly dedicated messages.
On job details, hiring stages may include an optional
`compensation: { amount, currency }` object. The timeline labels the stage as **eligible for a
one-time payment** after completion, separately from the job's recurring salary. Older API
responses that omit `compensation` render exactly as before. This template consumes that field
through `@startupkit-app/jobs` 0.4 or newer.
## Project structure
```
app/
page.tsx # Job list (ISR 60s, tag "jobs") + filters + pagination
layout.tsx # Shell: header, footer, skip link
loading.tsx / error.tsx / not-found.tsx
opengraph-image.tsx # Branded OG image
jobs/[token]/
page.tsx # Job detail (ISR 300s, JSON-LD, generateStaticParams)
loading.tsx
apply/
page.tsx # Fetches the form schema server-side
apply-form.tsx # Client: dynamic field/question renderer, useActionState
actions.ts # Server Action: submitApplication
talent-pool/
page.tsx # Talent-pool schema + consent terms (ISR 300s)
signup-form.tsx # Client: consent-first signup form, useActionState
actions.ts # Server Action: joinTalentPool (forwards the consent IP)
api/revalidate/route.ts # Optional webhook → revalidateTag
components/
file-upload.tsx # Client: MD5 → presign → direct PUT with progress
kit-analytics.tsx # Client: <TrackJobBoardView /> + <TrackJobView /> page-view events
hiring-process.tsx # Responsive stage timeline + optional one-time payments
job-card.tsx job-filters.tsx salary.tsx empty-state.tsx turnstile.tsx
lib/
kit.ts # server-only client instance + re-exported SDK types
kit-tracker.ts # browser analytics tracker (publishable key, no-op without one)
kit-errors.ts # shared submission-failure copy + API field-error shaping
upload-actions.ts # Server Action: createFileUpload (presign), shared by both forms
md5.ts # vendored MD5 (base64) for upload checksums
jsonld.ts # schema.org JobPosting builder
sanitize.ts # HTML sanitizer for API-supplied markup
format.ts jobs.ts talent-pool.ts # display helpers, cached job + talent-pool fetchers
```
## Scripts
```bash
npm run dev # start the dev server
npm run build # production build
npm run start # serve the production build
npm run lint # eslint
npm run typecheck # tsc --noEmit
npm test # route/attribution helpers (Node 22.18+)
npm run test:browser # production browser regressions, mocked API only
```
For browser tests, install Chromium once with `npx playwright install chromium`.
The harness copies the app into an ignored temporary workspace, builds it against a local
fixture API, and checks the candidate flow both at `/` and with Next.js `basePath: "/careers"`.
It preserves your app and deployment configuration, excludes `.env*` files, and uses only
fixture applicants. GitHub Actions runs these checks on pull requests and `main` with Node 24.
## Changelog
Notable changes are recorded in [CHANGELOG.md](CHANGELOG.md).
## License
[MIT](LICENSE)
Explore similar templates
Compare previews and descriptions within the same stack and category.

SaaS-Boilerplate
SaaS-Boilerplate is an open-source SaaS starter template built with Next.js App Router, React 19, TypeScript, Tailwind CSS, and Shadcn UI.
Next.js · SaaS

Next-JS-Landing-Page-Starter-Template
A free Next.js landing page starter template written in Tailwind CSS and TypeScript, featuring built-in navbar, hero, features, CTA banner, and footer sections.
Next.js · Corporate Website

next-saas-starter
A free Next.js SaaS marketing landing page template from Blazity, using static generation and styled-components with no extra UI library.
Next.js · SaaS

startup-nextjs
Startup is a free, MIT-licensed Next.js template from NextJSTemplates for SaaS startups and corporate landing pages.
Next.js · SaaS

shadcn-landing-page
A Next.js landing page template built with Shadcn, TypeScript, and Tailwind, converted from a Shadcn-Vue version. It includes sections such as Navbar, mobile Sidebar, Hero, Pricing, FAQ, and Footer, and highlights responsive design, navigation, and dark mode.
Next.js · Corporate Website

shadcn-dashboard-landing-template
An open-source admin dashboard and marketing landing page template built on shadcn/ui and Tailwind CSS, with both Vite + React and Next.js versions in one repository.
Next.js · Admin dashboard
