Best Boilerplates for Job Board Platforms in 2026
·StarterPick Team
job-boardrecruitmentboilerplatesaas2026
Job Boards: A Recurring SaaS Category
Job boards are perpetually popular side projects and niche SaaS products. The formula works: free job listings attract candidates, employers pay to post and feature listings, and a niche focus (remote only, specific tech stack, specific industry) creates defensible audience.
Build vs Platform Decision
| Option | Cost | Time to Launch | Customization | Best For |
|---|---|---|---|---|
| Jobboard.io | $99/mo | 1 day | Low | Quick validation |
| Careerpage.co | $49/mo | 1 day | Medium | Company careers pages |
| Custom Next.js | Dev time | 1-2 weeks | Complete | Niche job board product |
Build custom when: niche-specific features, unique matching algorithms, or you're building a platform for others to create job boards.
Core Job Board Data Model
model JobPosting {
id String @id @default(cuid())
title String
company Company @relation(fields: [companyId], references: [id])
companyId String
description String @db.Text
requirements String? @db.Text
salary Json? // { min: 80000, max: 120000, currency: 'USD' }
location String?
remote RemoteType @default(HYBRID) // REMOTE, HYBRID, ONSITE
jobType JobType @default(FULL_TIME) // FULL_TIME, PART_TIME, CONTRACT
tags String[] // "react", "typescript", "senior"
featured Boolean @default(false)
published Boolean @default(false)
expiresAt DateTime
applications Application[]
createdAt DateTime @default(now())
@@index([remote, jobType, published]) // Critical for search performance
@@index([tags])
}
model Company {
id String @id @default(cuid())
name String
logo String?
website String?
size String? // "1-10", "11-50", "51-200", etc.
subscriptionId String? // Stripe subscription for job posting credits
postings JobPosting[]
}
Full-Text Search
Job boards need search across title, description, and company:
-- PostgreSQL full-text search
ALTER TABLE job_postings ADD COLUMN search_vector tsvector;
UPDATE job_postings SET search_vector =
to_tsvector('english', coalesce(title, '') || ' ' || coalesce(description, ''));
CREATE INDEX job_search_idx ON job_postings USING GIN(search_vector);
-- Query
SELECT * FROM job_postings
WHERE search_vector @@ plainto_tsquery('english', 'senior react developer')
AND published = true
ORDER BY featured DESC, ts_rank(search_vector, plainto_tsquery('english', 'senior react developer')) DESC;
Employer Billing Model
// Pay-per-post model
const POSTING_CREDITS = {
basic: { posts: 1, price: 99, featured: false },
featured: { posts: 1, price: 199, featured: true },
bundle5: { posts: 5, price: 399, featured: false },
};
// Subscription model (for frequent posters)
const SUBSCRIPTION_PLANS = {
startup: { postsPerMonth: 3, price: 149 },
growth: { postsPerMonth: 10, price: 399 },
enterprise: { postsPerMonth: 999, price: 999 },
};
RSS Feed for Aggregators
Job boards get traffic from job aggregators (Indeed, LinkedIn, Google for Jobs). Provide an RSS/XML feed:
// app/feed.xml/route.ts
export async function GET() {
const jobs = await db.jobPosting.findMany({
where: { published: true, expiresAt: { gt: new Date() } },
orderBy: { createdAt: 'desc' },
take: 100,
include: { company: true },
});
const feed = `<?xml version="1.0"?>
<rss version="2.0" xmlns:google="http://base.google.com/ns/1.0">
<channel>
<title>Jobs - YourJobBoard</title>
<link>https://yourjobboard.com</link>
${jobs.map(job => `<item>
<title>${job.title} at ${job.company.name}</title>
<link>https://yourjobboard.com/jobs/${job.id}</link>
<description>${job.description.slice(0, 500)}</description>
<pubDate>${job.createdAt.toUTCString()}</pubDate>
</item>`).join('')}
</channel>
</rss>`;
return new Response(feed, { headers: { 'Content-Type': 'application/xml' } });
}
Compare job board and SaaS boilerplates on StarterPick.
Check out this boilerplate
View Next.js Job Board on StarterPick →