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.

Before
<h1>Welcome to my website</h1>
After
<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
Code
Default content
Admin
Supabase
Website

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.

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.

hero_title:"Developers build. Clients edit."
Key
Value (text)

This key is used everywhere:

  • In your code
  • In admin panel
  • In database

How content flows

1. Checks for value in Supabase
2. If exists — uses remote value
3. Fallback to defaultContent (local code)

Example

1. Static (Before)

<h1>Welcome</h1>

2. Editable (After)

<h1>{c.hero_title}</h1>

3. Local Config (DefaulContent.ts)

hero_title:"Welcome"
Key
Value (text)

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

Public: Enabled

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

Step 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_password

What 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/api
lib
components/ui

Important: 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.tsx

Copy this file completely without any modifications.

Result check

Your project structure should now include:

API folder
Lib folder
UI components
Admin page
Env file

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:

app/page.tsx
components/pages/HomePage.tsx

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

Paste 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:

app/about/page.tsx
components/pages/AboutPage.tsx

6. Make Content Editable

Replace text

Replace static text with content keys.

Example:

<h1>Welcome</h1>

Becomes:

<h1>{c.hero_title}</h1>

Add to defaultContent

Add the key to defaultContent:

hero_title:"Welcome"
Key
Value (text)

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:

It will not appear in the admin panel.

Images

Use the same logic:

hero_image:"/image.png"
Key
Value (text)

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

Add 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.tsx

sections

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 install

Start dev server

$ pnpm dev

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 are ready.

You now have a fully editable website.