A simple way to turn Notion pages into a fast, clean website.
nosite lets you write content in Notion and turn it into static web pages at build time using Next.js.
No live Notion calls, no complicated setup, and no slow page loads.
🔗 Demo: https://nosite.ixtj.dev
You write your content in Notion.
nosite builds your site from that content and serves it as static pages.
This makes your site:
- Fast - Content is generated at build time, so pages load instantly
- Cheap to host - Static files can be hosted anywhere for free or very low cost
- Easy to reason about - No databases, no servers, no complex infrastructure
- Stable in production - No live API calls that can break or slow down
It's designed to be approachable, even if you're still learning web development.
- You already like writing in Notion
- You want a real website, not a no-code platform
- You don't want to deal with databases or CMS servers
- You want something you can understand, modify, and deploy yourself
- Fast pages - Content is generated at build time, so pages load instantly
- Notion as your editor - Write and edit content in Notion instead of Markdown or custom admin panels
- SEO-friendly by default - Pages are structured for search engines out of the box
- Modern tech, but not scary - Built with Next.js, TypeScript, and Tailwind — but you don't need to touch most of it to use the template
- Looks good on any device - Mobile-friendly layout with light and dark mode
- Homepage (
/) - Your main landing page (required) - Blog (
/blog) - Blog post listing and individual posts - Gallery (
/gallery) - Showcase photos, reviews, or portfolio items - Pricing (
/pricing) - Display your services and pricing information - Contact (
/contact) - Contact information and form
You can turn pages on or off by setting a few environment variables.
- Write content in Notion
- Run a command to generate the site
- Deploy
When you update content, you just rebuild. No live syncing, no surprises.
- Students learning web development
- Indie hackers and solo builders
- People who want a personal site or blog
- Anyone who wants control without overengineering
- People who want full Notion feature parity
- People who expect live syncing with Notion
- People building interactive or app-like experiences
- People who want maximum flexibility over clear constraints
Get your website up and running in just 5 minutes!
- Bun - JavaScript runtime and package manager
- Notion account with a workspace
- Basic familiarity with terminal commands
-
Clone the repository
git clone https://github.com/shanefully-done/nosite.git cd nosite -
Install dependencies
bun install
-
Duplicate the Notion template
Before setting up environment variables, duplicate the Notion template to your workspace:
- Visit the nosite template page
- Click "Duplicate" in the top-right corner to copy it to your Notion workspace
- This template includes example databases and pages for all supported features
- You can customize the content after duplicating
-
Set up environment variables
cp .env.example .env.local
Edit
.env.localand add:NOTION_TOKEN=your_notion_token_here HOMEPAGE_NOTION_ID=your_homepage_page_id_here NEXT_PUBLIC_SITE_URL=http://localhost:3000 NEXT_PUBLIC_SITE_NAME=My Website NEXT_PUBLIC_SITE_DESCRIPTION=A professional website powered by Notion
-
Get Notion page IDs
- After duplicating the template, open the page in your Notion workspace
- Copy the page ID from the URL (it's the part after
/workspace/)
-
Generate cache
bun run cache
-
Start development server
bun run dev
-
Open your browser Navigate to http://localhost:3000
That's it! You now have a working website powered by Notion! 🎉
Everything you need to know to set up and customize your site.
- Go to Notion My Integrations
- Click "New integration"
- Give it a name (e.g., "My Website")
- Select your workspace
- Copy the "Internal Integration Token" - this is your
NOTION_TOKEN
Important: Your integration won't be able to access your pages until you explicitly share them.
- Open each Notion page you want to use
- Click the
•••menu in the top-right - Select "Add connections"
- Find and select your integration
Notion page IDs are found in the URL when viewing a page:
https://notion.so/workspace/PAGE_ID?v=...
^^^^^^^^
This is your page ID
Copy the 32-character ID (including hyphens) and paste it into your .env.local file.
The homepage is always required and cannot be disabled.
- Create a Notion page with your homepage content
- Copy the page ID
- Set
HOMEPAGE_NOTION_IDin.env.local
To enable the blog:
- Create a Notion database (not a page)
- Add these required properties:
- Title (Title type)
- Slug (Text type) - URL-friendly identifier for each post
- Published Date (Date type)
- Description (Text type)
- Cover (Files type) - Optional cover image
- Published (Checkbox type) - Must be checked to appear
- Copy the database ID (found in the URL when viewing the database)
- Set
BLOG_NOTION_IDin.env.local
To enable the gallery page:
- Create a Notion database for your gallery items
- Add properties like:
- Title (Title type)
- Image (Files type)
- Description (Text type)
- Category (Select type)
- Copy the database ID
- Set
GALLERY_NOTION_IDin.env.local
To enable pricing or contact pages:
- Create a Notion page (not a database)
- Add your content
- Copy the page ID
- Set
PRICING_NOTION_IDorCONTACT_NOTION_IDin.env.local
The main configuration file is src/config/site.config.ts.
You can customize:
- Site name and description
- Which pages are enabled
- Navigation menu labels
- SEO metadata
- Social media links
The cache revalidation API allows you to automatically trigger cache updates by visiting a special URL. This is particularly useful when combined with GitHub's deployment hooks on platforms like Vercel.
How it works:
- When you visit the special API URL with your secret key, it updates a text file in your GitHub repository
- This change triggers a new deployment (if you've set up deployment hooks)
- The deployment runs the cache update script automatically
- Your site rebuilds with fresh content from Notion
Setup Instructions:
-
Create a GitHub Personal Access Token:
- Go to GitHub Settings → Developer settings → Personal access tokens → Tokens (classic)
- Generate a new token with
reposcope - Copy the token - you'll need it for
GITHUB_TOKEN
-
Generate a secure UPDATE_KEY:
- Create a random, secret string that will be your API key
- You can generate one using:
openssl rand -hex 32or a password manager - Treat this like a password - never share it publicly!
-
Add environment variables to your deployment:
# Cache revalidation configuration GITHUB_TOKEN=your_github_personal_access_token UPDATE_KEY=your_random_secret_key GITHUB_OWNER=your_github_username GITHUB_REPO=your_repository_name GITHUB_UPDATE_FILE_PATH=update.txt GITHUB_BRANCH=master
-
Set up your API URL:
- Your API endpoint will be:
https://yourdomain.com/api/[UPDATE_KEY] - Example:
https://mysite.com/api/abc123def456...
- Your API endpoint will be:
-
Using the API:
Option A: Manual URL visit
- Simply visit the API URL in your browser
- The cache will update and trigger a rebuild
Option B: Add to Notion (Private Pages Only!)
- Create a button or link in your private Notion workspace
⚠️ CRITICAL: Only add this to PRIVATE Notion pages- Never add this link to public pages or share it publicly
- Example button text: "🔄 Refresh Website Cache"
Option C: Automate with Webhooks
- Use tools like Zapier or n8n to trigger the URL on schedules
- Set up cron jobs to automatically refresh content
- Integrate with other systems
Security Best Practices:
- 🔒 Keep UPDATE_KEY secret - Treat it like a password
- 🔒 Never expose it publicly - Don't commit it to public repos
- 🔒 Use long, random strings - At least 32 characters recommended
- 🔒 Only add to private Notion pages - Never to public content
- 🔒 Rotate keys periodically - Change your UPDATE_KEY regularly
- 🔒 Monitor deployment logs - Check for unauthorized cache updates
Troubleshooting:
- If the API returns "Not found", check that UPDATE_KEY matches exactly
- If you see "Server configuration error", verify all GitHub environment variables are set
- Check GitHub token has proper
repopermissions - Ensure the repository branch name matches your setup
You can update your content in two ways:
Option A: Manual Cache Update
- Update your content in Notion
- Run the cache update script:
bun run cache
- Rebuild your site if in production:
bun run build
Option B: Using the Cache Revalidation API
- Update your content in Notion
- Visit your API endpoint URL:
https://yourdomain.com/api/[UPDATE_KEY] - Wait for the deployment to complete
- Your site will automatically rebuild with fresh content
# Update cache (including downloading images) and build
bun run build
# Start production server
bun run startAutomatic Image Downloading:
When you run bun run build, the build process automatically:
- Downloads all images from Notion URLs to
public/images/ - Generates hash-based filenames (e.g.,
a3f2c9d1.jpg) - Replaces remote URLs in cached content with local paths
- Creates a mapping file in
cache/image-mapping.json
This ensures your images are served locally and won't expire.
Manual Image Download:
If you want to download images without rebuilding:
bun run download-imagesThis will:
- Scan all cache files for image URLs
- Download images to
public/images/ - Create/update
cache/image-mapping.json
How It Works:
- Images are downloaded from:
*.notion.so,images.unsplash.com,*.amazonaws.com - If download fails, the remote URL is used as fallback
- Hash-based filenames prevent naming conflicts
- Local images take precedence over remote URLs
- Build continues even if some images fail to download (with warnings)
Updating Images:
When you change images in Notion:
- Run
bun run cache(updates content cache) - Images are automatically downloaded and replaced
- Rebuild and deploy as usual
Troubleshooting Image Issues:
- If images don't load: Check
cache/image-mapping.jsonexists and is valid - If you see warnings: Some images failed to download (using remote URLs as fallback)
- To redownload all images: Delete
public/images/directory and runbun run cacheagain
- Fork this repository
- Import your repository
- Add environment variables (from your
.env.local) - Click "Deploy"
This template works on any platform that supports Next.js:
- Netlify: Build command
bun run build, Publish directory.next - AWS Amplify: Next.js preset, Build command
bun run build - Railway: Build command
bun run build, Start commandbun run start
Important: Next.js only allows images from specified hosts to be displayed on your site. If your images fail to load, you may need to add your image host to the allowed list.
The current allowed image hosts are configured in next.config.ts:
*.notion.so- All Notion-hosted imagesimages.unsplash.com- Unsplash stock photos*.amazonaws.com- AWS S3 and CloudFront images
To add a new image host:
- Open
next.config.ts - Add a new entry to the
remotePatternsarray:
images: {
remotePatterns: [
// ... existing patterns ...
{
protocol: "https",
hostname: "your-image-host.com", // Add your host here
port: "",
pathname: "/**",
},
],
},- Restart your development server after making changes
Common image hosts you might need:
- Cloudinary:
*.cloudinary.com - Cloudflare Images:
*.cloudflareimages.com - Imgur:
i.imgur.com - Custom CDN: Your specific domain
Pages are automatically enabled based on whether their Notion ID is set:
- Homepage: Always enabled (required)
- Blog: Enabled if
BLOG_NOTION_IDis set - Gallery: Enabled if
GALLERY_NOTION_IDis set - Pricing: Enabled if
PRICING_NOTION_IDis set - Contact: Enabled if
CONTACT_NOTION_IDis set
To disable a page, remove or comment out its environment variable.
Required variables:
NOTION_TOKEN- Your Notion API tokenHOMEPAGE_NOTION_ID- Your homepage page IDNEXT_PUBLIC_SITE_URL- Your website URL
Optional variables (for enabling pages):
BLOG_NOTION_ID- Blog posts database IDGALLERY_NOTION_ID- Gallery database IDPRICING_NOTION_ID- Pricing page IDCONTACT_NOTION_ID- Contact page ID
Optional variables (for customization):
NEXT_PUBLIC_SITE_LANGUAGE- Language preference for navigation and UI labels"ko-en"(default) - Korean with English fallback"ko"- Korean only"en"- English only
See .env.example for all available options.
The template uses Tailwind CSS for styling. You can customize:
- Global styles: Edit
src/app/globals.css - Component styles: Each component has its own Tailwind classes
UI components are in src/components/ui/ and are built with Radix UI. You can:
- Modify existing components
- Add new components
- Change component variants
The main layout is in src/components/layout.tsx. You can customize:
- Navigation menu
- Footer content
- Logo/branding
Problem: You see "Content not found" messages on your pages.
Solution:
- Make sure you've shared your Notion pages with your integration
- Check that your Notion IDs are correct
- Run
bun run cacheto update the cache - Verify the integration token is valid
Problem: Content changes in Notion don't appear on your site.
Solution:
- Run
bun run cacheto rebuild the cache - If in development, restart the dev server
- If in production, rebuild and redeploy
Problem: The build fails with errors.
Solution:
- Check that all required environment variables are set
- Verify Notion IDs are correct
- Make sure cache files exist (run
bun run cache) - Check the error message for specific issues
Problem: A page is enabled but doesn't appear in navigation.
Solution:
- Check the page is enabled in
src/config/site.config.ts - Verify the environment variable is set
- Check the page file exists in
src/app/
MIT. Free to use, modify, and ship.