Settings The 28 settings that control your site without a redeploy.
Everything configurable lives in /admin/settings — a single site_settings table keyed by setting name. Changes apply instantly because reads are cached via unstable_cache with a "site-settings" tag revalidated on each save.
Schema in lib/settings-shared.ts — type, default, label, group, description per key
Admin UI auto-generates inputs from the schema
Values stored as text in site_settings(key, value) with updated_at
Server reads via:
getSetting(key) — single setting
getSettingNumber(key) — parses to number, returns 0 if missing
getSettings() — every setting with defaults
getSettingsTyped() — typed object with camelCase keys (ready for destructuring)
Key Purpose site_nameUsed in header, emails, meta title site_taglineShort tagline (home, meta) site_descriptionLonger description (meta tags, OG) contact_emailPublic contact (footer, emails)
Key Purpose launch_dayISO day the weekly batch expects to publish on launch_hour_utcUTC hour free_slots_per_batchMax free products per batch (overflow stays queued) highlight_duration_daysLifetime of a Highlight pin dofollow_top_nTop N free products promoted to dofollow at publish
Key Purpose boost_price_centsBoost tier price highlight_price_centsHighlight tier price
Key Purpose sponsor_slot_countSidebar slots sponsor_base_price_centsFull price per day per slot sponsor_max_discountMax discount % at sponsor_discount_full_days sponsor_discount_full_daysDays at which max discount applies sponsor_max_daysMax booking duration
Key Purpose votes_per_windowVote rate limit count votes_window_secondsVote rate limit window submissions_per_daySubmit rate limit (per user/day) max_thumbnail_size_mbUpload size cap related_products_limit"Related products" section size admin_products_per_pagePage size in /admin/products comments_per_windowComment rate limit count comments_window_secondsComment rate limit window
Key Purpose twitter_handleUsed in Twitter Card meta
Key Purpose font_sansBody font (Google Fonts name) font_serifHeading/serif font (Google Fonts name)
Add an entry to SETTINGS_SCHEMA in lib/settings-shared.ts :
my_new_setting : {
default : "value" ,
type : "string" ,
label : "My new setting" ,
description : "What this does" ,
group : "general" ,
}
Restart dev — the admin UI regenerates automatically
Read server-side:
const value = await getSetting ( "my_new_setting" )
Supported types: string, number, boolean.
Rule of thumb:
Secrets and per-environment config → env vars (STRIPE_SECRET_KEY, DATABASE_URL)
Public, admin-tweakable values → settings (site_name, boost_price_cents)
Never store secrets in settings — they end up in plaintext in Postgres and are visible to anyone with DB access.
Settings are cached with Next.js's unstable_cache tagged "site-settings". Every save calls revalidateTag("site-settings") so every subsequent read sees fresh data. No manual cache invalidation needed.