Modern, fast, and beautiful blog software powered by Nuxt UI v4 and Cloudflare Workers
NuxtPress is a modern blogging platform that's:
- Fast: Built on Cloudflare's edge network for lightning-fast performance worldwide
- Simple: Easy to deploy and use with a clean, intuitive interface
- Secure: Password-protected admin panel with session management
- Beautiful: Powered by Nuxt UI v4 for a stunning, responsive design
- Free: Deploy on Cloudflare's free tier
- Click the "Deploy to NuxtHub" button above
- Connect your GitHub account
- NuxtHub will automatically:
- Fork this repository to your account
- Set up Cloudflare Workers integration
- Deploy your blog
- Provision the database and KV storage
- Configure your blog settings (see below)
- Start writing!
If you prefer to deploy manually:
- Fork this repository
- Create a NuxtHub account
- Link your repository to NuxtHub
- Deploy from the NuxtHub dashboard
- Configure environment variables (see below)
After deployment, you'll need to configure your blog through environment variables:
| Variable | Description | Default |
|---|---|---|
NUXT_PASSWORD |
Admin password for logging in | password |
These can be configured via environment variables or through the admin panel after logging in:
| Variable | Description | Default |
|---|---|---|
NUXT_PUBLIC_SITE_URL |
Your blog's public URL | https://nuxtpress.pages.dev |
NUXT_PUBLIC_NAME |
Your blog name | NuxtPress |
NUXT_PUBLIC_DESCRIPTION |
Blog description | My NuxtPress blog |
NUXT_PUBLIC_AUTHOR |
Author name | Gregory Mitchell |
NUXT_PUBLIC_THEME_COLOR |
Theme color (hex) | #1e40af |
NUXT_PUBLIC_FAVICON |
Favicon URL | /_favicon.ico |
NUXT_PUBLIC_FAVICON_PNG |
PNG favicon URL | /_favicon.png |
NUXT_PUBLIC_GITHUB |
GitHub profile URL | (empty) |
NUXT_PUBLIC_INSTAGRAM |
Instagram profile URL | (empty) |
NUXT_PUBLIC_TWITTER |
Twitter/X profile URL | (empty) |
NUXT_PUBLIC_PATREON |
Patreon profile URL | (empty) |
NUXT_PUBLIC_LINKEDIN |
LinkedIn profile URL | (empty) |
NUXT_PUBLIC_DISCORD |
Discord server URL | (empty) |
NUXT_PUBLIC_SUPPORT_EMAIL |
Support email address | (empty) |
How to set environment variables in NuxtHub:
- Go to your project on hub.nuxt.com
- Navigate to Settings β Environment Variables
- Add your variables
- Redeploy your application
If you used this repository as a template and want to pull updates from the original template repository, you can add the template as a remote and merge updates into your repository. The following snippet shows the minimal commands you may use.
# one time
git remote add template https://github.com/gmitch215/nuxtpress
# to install an update
git switch master
git fetch --all
git merge template --allow-unrelated-histories -m "chore: merge upstream"Notes:
- The first
git remote addonly needs to be run once per cloned repository. - Switch to the branch you want to update (here
master) before merging. --allow-unrelated-historiesis included to allow merging between repositories that do not share history; resolve conflicts if they appear.- Always create a local backup branch before merging so you can recover easily if something goes wrong:
git switch -c before-template-mergeExamples for targeting a specific branch, tag, or commit
- Merge a specific branch from the template (e.g.
experimental):
git fetch template
git switch master
git merge template/experimental --allow-unrelated-histories -m "chore: merge template/experimental"- Merge a specific tag from the template (e.g.
v1.0.2):
git fetch --tags template
git switch master
git merge template/v1.0.2 --allow-unrelated-histories -m "chore: merge template v1.0.2"If you prefer to create a local branch that mirrors the tagged state, you can do:
git fetch --tags template
git checkout -b update-v1.0.2 template/v1.0.2
# Inspect changes, then merge into master if desired
git switch master
git merge update-v1.0.2- Apply a single commit from the template (e.g.
ab12ef3):
git fetch template
git switch master
git cherry-pick ab12ef3Alternatives and safety tips:
- Instead of merging directly into
master, consider creating a temporary update branch (git switch -c update-from-template) and test there before merging to your main branch. - If the template remote is updated frequently, run
git fetch templateto refresh refs before merging. - If you encounter conflicts, resolve them and then
git committo complete the merge.
- Navigate to your blog URL
- Click the login button in the navigation
- Enter your password (the one you set in
NUXT_PASSWORD)
- Log in to your blog
- Click "New Post" or navigate to
/create - Fill in:
- Title: Your post title
- Slug: URL-friendly version (auto-generated from title)
- Content: Your post content (supports Markdown)
- Tags: Comma-separated tags
- Thumbnail (optional): Upload an image
- Click "Publish"
- Edit: Click on any post while logged in to edit it
- Delete: Use the delete button on a post's edit page
- View: All posts are automatically listed on your homepage
- Log in to your blog
- Navigate to
/settings - Update your blog information, social links, and appearance
- Changes are saved in your Cloudflare KV storage and take effect immediately
NuxtPress is a full-stack blogging platform built with:
- Frontend: Nuxt 4 with Vue 3, Nuxt UI v4, and Tailwind CSS
- Backend: Nuxt server routes with Cloudflare Workers
- Database: Cloudflare D1 (SQLite on the edge)
- Storage: Cloudflare KV for caching and settings
- Validation: Zod schemas for type-safe data validation
- Deployment: NuxtHub for seamless Cloudflare integration
- Nuxt 4: Vue.js framework with SSR/SSG
- Nuxt UI v4: Beautiful UI component library
- @nuxthub/core: Cloudflare Workers integration
- Cloudflare D1: Edge SQL database
- Cloudflare KV: Edge key-value storage
- Zod: TypeScript-first schema validation
- Luxon: Modern date/time handling
- Tailwind CSS v4: Utility-first CSS framework
- Bun or Node.js 18+
- A Cloudflare account (for deployment)
-
Clone the repository:
git clone https://github.com/gmitch215/nuxtpress.git cd nuxtpress -
Install dependencies:
bun install
-
Set up environment variables:
cp .env.example .env
Edit
.envand set yourNUXT_PASSWORDand other configuration. -
Run the development server:
bun run dev
bun run dev- Start development server on port 8787bun run dev:test- Start dev server with test environmentbun run build- Build for productionbun run preview- Preview production build locally with NuxtHubbun run prettier- Format code with Prettierbun run prettier:check- Check code formatting
nuxtpress/
βββ src/
β βββ app.vue # Root application component
β βββ error.vue # Error page component
β βββ assets/ # Static assets
β β βββ css/main.css # Global styles
β βββ components/ # Vue components
β β βββ BlogForm.vue # Blog post creation/edit form
β β βββ BlogPostGroup.vue # Blog post listing component
β β βββ Footer.vue # Site footer
β β βββ LoginForm.vue # Authentication form
β β βββ NavBar.vue # Navigation bar
β β βββ SettingsForm.vue # Settings management form
β βββ composables/ # Vue composables
β β βββ useDatabase.ts # Database utilities
β β βββ useLogin.ts # Authentication utilities
β βββ layouts/ # Nuxt layouts
β β βββ default.vue # Default layout
β βββ pages/ # File-based routing
β β βββ index.vue # Homepage (blog list)
β β βββ [year]/ # Date-based blog routes
β β βββ index.vue # Posts by year
β β βββ [month]/
β β βββ index.vue # Posts by month
β β βββ [day]/
β β βββ index.vue # Posts by day
β β βββ [slug].vue # Individual post
β βββ server/ # Server-side code
β β βββ utils.ts # Server utilities
β β βββ api/ # API routes
β β βββ login.post.ts # User login
β β βββ logout.post.ts # User logout
β β βββ verify.get.ts # Session verification
β β βββ settings.get.ts # Get settings
β β βββ settings.post.ts # Update settings
β β βββ blog/ # Blog API endpoints
β β βββ create.post.ts # Create post
β β βββ find.get.ts # Find post by ID/slug
β β βββ list.get.ts # List all posts
β β βββ update.patch.ts # Update post
β β βββ remove.delete.ts # Delete post
β βββ shared/ # Shared utilities
β βββ schemas.ts # Zod validation schemas
β βββ types.ts # TypeScript types
βββ public/ # Public static files
βββ nuxt.config.ts # Nuxt configuration
βββ package.json # Dependencies
βββ tsconfig.json # TypeScript configuration
All API routes are located in src/server/api/ and are automatically available at /api/*.
Authenticate with the admin password.
Request Body:
{
"password": "your-password"
}Response:
{
"ok": true
}Sets a secure session cookie valid for 30 days.
Invalidate the current session.
Response:
{
"ok": true
}Check if the current session is valid.
Response:
{
"loggedIn": true
}Get current blog settings (public endpoint).
Response:
{
"name": "My Blog",
"description": "Blog description",
"author": "Author Name",
"themeColor": "#1e40af",
"favicon": "/favicon.ico",
"faviconPng": "/favicon.png",
"github": "https://github.com/username",
"twitter": "https://twitter.com/username",
"instagram": "https://instagram.com/username",
"patreon": "https://patreon.com/username",
"linkedin": "https://linkedin.com/in/username",
"discord": "https://discord.gg/server-invite",
"supportEmail": "support@example.com"
}Update blog settings (requires authentication).
Request Body: Same as GET response (partial updates supported)
Response: Updated settings object
Get all blog posts (public endpoint).
Response:
[
{
"id": "unique-id",
"title": "Post Title",
"slug": "post-slug",
"content": "Post content...",
"thumbnail_url": "data:image/png;base64,...",
"created_at": "2025-11-17T00:00:00.000Z",
"updated_at": "2025-11-17T00:00:00.000Z",
"tags": ["tag1", "tag2"]
}
]Find a specific blog post by ID or slug.
Query Parameters:
id(optional): Post IDslug(optional): Post slug
Response: Single blog post object (same structure as list)
Create a new blog post (requires authentication).
Request Body:
{
"post": {
"title": "Post Title",
"slug": "post-slug",
"content": "Post content...",
"thumbnail": "base64-encoded-image-data",
"tags": ["tag1", "tag2"]
}
}Response: Created blog post object
Note: Slugs are automatically made unique by appending -1, -2, etc. if duplicates exist.
Update an existing blog post (requires authentication).
Request Body:
{
"id": "post-id",
"post": {
"title": "Updated Title",
"content": "Updated content...",
"tags": ["updated-tags"]
}
}Response: Updated blog post object
Delete a blog post (requires authentication).
Query Parameters:
id: Post ID to delete
Response:
{
"ok": true
}NuxtPress uses Cloudflare D1 with the following schema:
CREATE TABLE IF NOT EXISTS blog_posts (
id TEXT PRIMARY KEY,
title TEXT NOT NULL,
slug TEXT NOT NULL UNIQUE,
content TEXT NOT NULL,
thumbnail BLOB,
created_at INTEGER NOT NULL,
updated_at INTEGER NOT NULL,
tags TEXT NOT NULL
);Fields:
id: Unique identifier (UUID)title: Post titleslug: URL-friendly slug (must be unique)content: Post content (Markdown supported)thumbnail: Optional image thumbnail (stored as BLOB)created_at: Unix timestamp of creationupdated_at: Unix timestamp of last updatetags: JSON-encoded array of tags
Contributions are welcome! Here's how to get started:
-
Fork the repository
git clone https://github.com/YOUR-USERNAME/nuxtpress.git
-
Create a feature branch
git checkout -b feature/amazing-feature
-
Make your changes
- Follow the existing code style
- Run
bun run prettierbefore committing - Ensure types are correct with TypeScript
-
Commit your changes
git commit -m "feat: add amazing feature"Follow Conventional Commits format.
-
Push and create a Pull Request
git push origin feature/amazing-feature
- Code Style: Prettier is configured and runs automatically on commit via Husky
- TypeScript: Strict mode is enabled; ensure all types are correct
- Components: Use Nuxt UI components when possible for consistency
- API Routes: Use Zod schemas for validation
- Server Utils: Helper functions are in
src/server/utils.ts - Database: Always use parameterized queries to prevent SQL injection
This project is open source and available under the MIT License.