Next.js Admin Panel Documentation
What is this
This is a system that allows you to make your website content editable without using a CMS.
Instead of hardcoding text directly in your components, you replace it with special keys.
<h1>Welcome to my website</h1><h1>{c.hero_title}</h1>Now this text can be changed from an admin panel without touching the code.
Important: You still keep full control over your layout, design, and logic. Only the content becomes editable.
How it works
The system works in a very simple way:
- You replace text in your code with keys
- Default values are stored in
defaultContent.ts - Supabase stores updated values
- Admin panel lets you edit content
- Website always shows the latest version
Zero-downtime fallback: If a value is missing in Supabase or the database is unreachable, the website instantly uses defaultContent from your local code.
What you are going to do
You are not creating a new project.
You are adding this system to your existing Next.js website.
- Connect Supabase (database)
- Add environment variables
- Copy required files into your project
- Copy required files into your project
- Replace text with content keys
- Configure admin panel
After that, you will be able to edit your website content from the /admin page.
Want a faster setup? Skip the detailed guide and use the GitHub README instead.
Before you start
Make sure you have:
- A Next.js project
- Access to your code
- Basic understanding of React components
Estimated setup time: ~20–40 minutes depending on your project size
Important: This system works only with Next.js applications.
1. Core Concept
What is content system
This system is built around three main parts:
- Keys — unique identifiers in your code
- Default content — local fallback for development
- Supabase — remote storage for production
Each piece of text on your website is linked to a key.
This key is used everywhere:
- In your code
- In admin panel
- In database
How content flows
Example
1. Static (Before)
<h1>Welcome</h1>2. Editable (After)
<h1>{c.hero_title}</h1>3. Local Config (DefaulContent.ts)
Mental model
- Code— Defines structure & logic
- Keys— Content placeholders
- Admin— Visual editor
- Supabase— Remote storage
2. Supabase Setup
Step 1. Create account
Go to https://supabase.com
Click "Start your project"
Sign in using GitHub or email.
Step 2. Create project
Click "New project" and fill in the details:
Name
Name of your project
Password
Strong pwd
Region
Closest to you
Wait 1–2 minutes until the project is ready.
Step 3. Create table
Open SQL Editor, paste this code and run it:
create table content (
key text primary key,
value text
);The key column must be the PRIMARY KEY. This ensures every entry is unique.
Step 4. Create storage
Go to Storage → Create bucket
Name: images
This is required to store uploaded images.
Step 5. Get API keys
Go to Settings API and copy these values:
- Project URL
- anon public key
You will need them in the next step.
3. Environment Variables
Step 1. Create file
Create a file in your project root:
.env.localStep 2. Add variables
Paste the following keys from Supabase settings:
NEXT_PUBLIC_SUPABASE_URL=your_url
NEXT_PUBLIC_SUPABASE_ANON_KEY=your_key
SUPABASE_SERVICE_ROLE_KEY=your_key
ADMIN_PASSWORD=your_passwordWhat is ADMIN_PASSWORD
This is the password used to access the /admin page.
It acts as a simple gatekeeper: without it, the admin panel is inaccessible. Use a strong password to keep your content safe.
Add to hosting
When deploying, you must add these variables to your platform settings.
Vercel:
Project Settings Environment Variables
Netlify:
Site settings Environment variables
Warning:
If you skip this, the admin panel will not work in production. Your website won't be able to connect to Supabase.
Security Note
You might see warnings about NEXT_PUBLIC_ variables. This is intentional. These keys are safe for the frontend, while the SERVICE_ROLE_KEY is used only on the server to keep your data secure.
4. Install Files
Step 1. Download source
Go to the GitHub repository and download the project as a ZIP file. Extract it on your computer.
Click CodeDownload ZIP
Step 2. Copy required folders
Copy these folders into your project root:
app/apilibcomponents/uiImportant: Maintain the exact folder structure. Do not rename or move files — internal imports rely on these specific paths.
Step 3. Copy admin page
Copy the main admin file to your app directory:
app/admin/page.tsxCopy this file completely without any modifications.
Result check
Your project structure should now include:
If something is missing, go back and check the steps.
5. Project Structure
Why change structure?
- Separate logic fetching from routing
- Keep Next.js server components simple
- Enable live-preview sync in components
Step 1. Move page
Move your current page content to a new component:
Step 2. Replace page.tsx
Now, update your app/page.tsx to fetch content:
import HomePage from '@/components/pages/HomePage'
import { getContent } from '@/lib/getContent'
export const revalidate = 0
export default async function Page() {
const content = await getContent()
return <HomePage content={content} />
}export const revalidate = 0 // cache disabled for live updates
Step 3. Use the template
Create:
components/pages/HomePage.tsxPaste the following template (required):
'use client'
import { ContentKey } from '@/lib/defaultContent'
import { useEffect } from 'react'
type Content = Record<ContentKey, string>
type Props = {
content: Content
}
export default function Home({ content }: Props) {
useEffect(() => {
const handler = (e: MessageEvent) => {
if (e.data?.type !== 'scroll-to') return
const id = e.data.id
if (!id) return
const el = document.getElementById(id)
if (!el) return
const yOffset = -80
const y = el.getBoundingClientRect().top + window.pageYOffset + yOffset
window.scrollTo({
top: y,
behavior: 'smooth',
})
}
window.addEventListener('message', handler)
return () => window.removeEventListener('message', handler)
}, [])
const c = content
return (
<main>
<h1>{c.hero_title}</h1>
</main>
)
}Tip: This template includes a required useEffect for scroll synchronization. You can also find it in components/pages/TemplatePage.tsx.
Multi-page websites
Repeat this pattern for any additional pages:
6. Make Content Editable
Replace text
Replace static text with content keys.
Example:
Becomes:
<h1>{c.hero_title}</h1>Add to defaultContent
Add the key to defaultContent:
Important: defaultContent is your foundation.
It is not just a fallback — it defines your entire content structure.
- All keys must exist here
- Admin panel reads structure from here
- Website uses it when Supabase is empty
If a key is missing here, it will not work anywhere.
Important
If text is not replaced with a key:
Images
Use the same logic:
And then in your component:
<img src={c.hero_image} alt="Hero" />Important: Images uploaded via the admin panel:
- Are compressed
- Converted to WebP
- Optimized automatically
7. Multi-page
Multi-page setup
Since all keys are stored in a single table, use prefixes to organize content for different pages:
home_title: "..."
about_title: "..."
docs_title: "..."This keeps your keys unique and easy to find in the Admin Panel.
8. Next Config
Images configuration
You must allow external images in your project to render content from Supabase storage.
next.config.tsAdd the remotePatterns to your config file:
images: {
remotePatterns: [
{
protocol: 'https',
hostname: '**.supabase.co',
},
],
},Why this is required: Next.js blocks external images by default for security. This configuration allows your app to safely load optimized images from your Supabase bucket.
9. Admin Configuration
File
app/admin/page.tsxsections
Groups fields into logical blocks.
Example:
export const sections = {
Hero: [
'hero_title',
'hero_subtitle',
],
Demo: [
'demo_title',
'demo_subtitle'
],
Problem: [
'problem_title',
'problem_text',
],
}This makes editing structured and easier to manage.
labelMap
Controls how field names appear in the UI.
export const labelMap: Record<ContentKey, string> = {
hero_title: 'Hero Title',
hero_subtitle: 'Hero Subtitle',
demo_title: 'Demo Title',
demo_subtitle: 'Demo Subtitle',
problem_title: 'Problem Title',
problem_text: 'Problem Text',
}You can use any language.
scrollMap
Links the editor with the preview.
const scrollToSection = (section: string) => {
const map: Record<string, string> = {
Hero: 'hero',
Demo: 'demo',
Problem: 'problem',
}
etc...
}<section id='hero'> Your Content </section>When you open a section, the preview scrolls to the corresponding block.
Important
You do not need to write this from scratch. Just edit the file you copied. Change keys and labels as needed.
10. Run Project
Install dependencies
$ pnpm installStart dev server
$ pnpm devOpen admin panel
11. Using Admin Panel
Using admin panel
- Live preview
- Edit text
- Save changes
- Reset to default
- Upload images
Works on both mobile and desktop.
12. Common Mistakes
Common mistakes
- Text was not replaced with keys
- Missing environment variables
- Supabase is not configured
13. FAQ
Questions
Q: Can I use this without Supabase?
A: No
Q: Is it secure?
A: It is protected by a password layer.
14. Final
You now have a fully editable website.