WordPress Plugin + Next.js marketing site + Neon PostgreSQL
This template produces two artifacts:
- WordPress plugin — the core product, built from
wordpress-plugin/ - Marketing site — a Next.js app at the repo root, auto-deployed to Vercel on push to main
# Marketing site
npm install
npm run dev
# Plugin — no build step needed. WordPress plugins are raw PHP.
# Test by symlinking or copying wordpress-plugin/ into a local WordPress install's wp-content/plugins/The plugin lives entirely in wordpress-plugin/. It uses plain PHP — no build step, no compilation.
wordpress-plugin/
plugin-slug.php # Main plugin file (WordPress header, bootstrap)
includes/ # PHP classes
class-plugin-slug.php # Core plugin class
assets/
css/admin.css # Admin styles
js/admin.js # Admin scripts
readme.txt # WordPress.org readme (required format)
uninstall.php # Cleanup on plugin deletion
Replace before publishing:
{{PLUGIN_NAME}}— human-readable name{{PLUGIN_DESCRIPTION}}— short description{{PLUGIN_SLUG}}— kebab-case slug (file names, function prefixes, text domain){{PLUGIN_SLUG_UPPER}}— UPPER_SNAKE_CASE (PHP constants){{PLUGIN_SLUG_FUNC}}— snake_case (function prefixes){{PLUGIN_SLUG_CLASS}}— Class name format- Rename
plugin-slug.phpandclass-plugin-slug.phpto use your actual slug
See AGENTS.md for the full placeholder list and development guide.
Next.js 16 (app router) + Tailwind CSS, auto-deploys to Vercel on push to main.
PostgreSQL via Neon. The DATABASE_URL env var is pre-configured in Vercel.
Edit db/schema.ts:
import { pgTable, serial, text, timestamp } from "drizzle-orm/pg-core";
export const licenseKeys = pgTable("license_keys", {
id: serial("id").primaryKey(),
key: text("key").notNull().unique(),
email: text("email").notNull(),
active: text("active").notNull().default("true"),
createdAt: timestamp("created_at").defaultNow(),
});Schema changes are auto-applied to the database when merged to main via GitHub Actions.
import { db } from "@/db";
import { licenseKeys } from "@/db/schema";
const allKeys = await db.select().from(licenseKeys);
await db.insert(licenseKeys).values({ key: "abc-123", email: "user@example.com" });On every merge to main that changes wordpress-plugin/, a GitHub Action builds the plugin zip and attaches it to a GitHub Release. If wordpress-plugin/.wordpress-org-slug exists, it also syncs to the WordPress.org SVN repository.
- All database tables must be defined in
db/schema.ts - Marketing site auto-deploys on push to main (including db schema)
- Bump
Version:in the main plugin file ANDStable tag:inreadme.txtbefore each release - All PHP must follow WordPress coding standards (see AGENTS.md)
These files are pre-configured infrastructure:
db/index.ts— database connectiondrizzle.config.ts— database sync config.github/workflows/push-db-schema.yml— auto-applies schema on merge.github/workflows/deploy-plugin.yml— builds zip and syncs to WordPress.org.env.example— documents environment variables