Next.jsReactCorporate Website

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.

2 previews
Desktop homepage1 / 2
Desktop captureTop of page

Actual screenshot captured by the server browser.

All pages & views 2

Screenshots show visual appearance; interactive behavior may differ.

Recorded 2026-10-06View source

What stands out

A quick read of the recorded project description.

01

Job list supports URL parameter filtering by department, location, employment type, remote, etc., with pagination

02

Job detail pages include salary range, hiring process timeline, one-time milestone payment labeling, and schema.org JobPosting JSON-LD

03

Application form dynamically rendered from the API schema, covering 8 field types and 3 question types, with inline validation and accessibility support

04

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

05

Optional Cloudflare Turnstile anti-spam submission and Webhook cache invalidation; these do not take effect when keys are not configured

06

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.

Next.jsCorporate website

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. 1

    Create keys in Kit under Hiring → Career Portal → Public API Keys; deployment requires STARTUPKIT_SECRET_KEY (sk_…)

  2. 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. 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. 4

    Enabling analytics requires a publishable key (pk_…); if the key has an origin whitelist, the site domain must be in the whitelist

  5. 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

Read repository instructions

License & source material

Usage conditions & limitations

Free to use · keep license notice

This 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

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.

Original README
https://github.com/startupkit-app/nextjs-job-board/blob/main/LICENSEhttps://github.com/startupkit-app/nextjs-job-board/blob/main/README.md

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.

[![Deploy with Vercel](https://vercel.com/button)](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)
Report a correction

Explore similar templates

Compare previews and descriptions within the same stack and category.