Skip to main content

Project Structure

Understand the FlatWP Starter template structure and file organization.

Overview

The FlatWP Starter is organized for clarity and scalability:

flatwp-starter/
├── app/ # Next.js App Router
│ ├── (pages)/ # Page routes
│ │ └── [slug]/ # Dynamic pages from WordPress
│ ├── blog/ # Blog routes
│ │ ├── [slug]/ # Individual posts
│ │ ├── category/ # Category archives
│ │ └── tag/ # Tag archives
│ ├── api/ # API routes
│ │ ├── revalidate/ # ISR revalidation endpoint
│ │ └── preview/ # Preview mode handler
│ ├── layout.tsx # Root layout
│ ├── page.tsx # Homepage
│ └── globals.css # Global styles
├── components/ # React components
│ ├── blocks/ # WordPress block components
│ │ ├── core/ # Core Gutenberg blocks
│ │ └── flatwp/ # FlatWP custom blocks
│ ├── blog/ # Blog-specific components
│ ├── layout/ # Header, Footer, Navigation
│ └── ui/ # Shadcn/ui components
├── lib/ # Utilities and helpers
│ ├── wordpress/ # WordPress client & adapters
│ ├── blocks/ # Block registry & utilities
│ └── utils/ # General utilities
├── graphql/ # GraphQL configuration
│ ├── queries/ # GraphQL query files
│ ├── fragments/ # Reusable GraphQL fragments
│ └── generated/ # Auto-generated types
├── public/ # Static assets
├── scripts/ # Build and setup scripts
├── .env.example # Environment template
├── next.config.ts # Next.js configuration
├── tailwind.config.ts # Tailwind CSS configuration
├── codegen.ts # GraphQL Code Generator config
└── package.json # Dependencies and scripts

Directory Details

/app - Next.js App Router

The app directory uses Next.js 14+ App Router conventions.

Route Groups

app/
├── (pages)/ # Grouping for WordPress pages
│ └── [slug]/ # Dynamic route for all pages
│ └── page.tsx
├── (marketing)/ # Marketing pages (optional)
│ ├── about/
│ ├── pricing/
│ └── contact/
└── blog/ # Blog section
├── page.tsx # Blog index
└── [slug]/ # Individual posts

Key Files

FilePurpose
layout.tsxRoot layout with header/footer
page.tsxHomepage component
not-found.tsx404 error page
error.tsxError boundary
loading.tsxLoading skeleton

/app/api - API Routes

Server-side API endpoints for WordPress integration:

// app/api/revalidate/route.ts
export async function POST(request: Request) {
const { secret, paths } = await request.json();

if (secret !== process.env.FLATWP_SECRET) {
return Response.json({ error: 'Invalid secret' }, { status: 401 });
}

// Revalidate paths
for (const path of paths) {
revalidatePath(path);
}

return Response.json({ revalidated: true, paths });
}

/components - React Components

Organized by feature and purpose:

components/
├── blocks/ # WordPress block renderers
│ ├── core/ # Core Gutenberg blocks
│ │ ├── ParagraphBlock.tsx
│ │ ├── HeadingBlock.tsx
│ │ ├── ImageBlock.tsx
│ │ └── ...
│ ├── flatwp/ # FlatWP custom blocks
│ │ ├── HeroBlock.tsx
│ │ ├── FeaturesBlock.tsx
│ │ ├── CTABlock.tsx
│ │ └── ...
│ └── EditorBlockRenderer.tsx # Main block renderer
├── blog/ # Blog components
│ ├── PostCard.tsx
│ ├── PostGrid.tsx
│ ├── CategoryList.tsx
│ └── Sidebar.tsx
├── layout/ # Layout components
│ ├── Header.tsx
│ ├── Footer.tsx
│ ├── Navigation.tsx
│ └── MobileMenu.tsx
└── ui/ # Shadcn/ui components
├── button.tsx
├── card.tsx
└── ...

/lib - Utilities & Libraries

Core functionality and helpers:

lib/
├── wordpress/ # WordPress integration
│ ├── client.ts # GraphQL client
│ ├── queries.ts # Query helpers
│ └── adapters/ # Data transformers
│ ├── pages.ts
│ ├── posts.ts
│ └── menus.ts
├── blocks/ # Block system
│ ├── registry.ts # Block component registry
│ └── utils.ts # Block utilities
└── utils/ # General utilities
├── cn.ts # Class name helper
└── formatting.ts # Date/text formatting

WordPress Client

// lib/wordpress/client.ts
import { GraphQLClient } from 'graphql-request';

const endpoint = process.env.NEXT_PUBLIC_WORDPRESS_API_URL!;

export const client = new GraphQLClient(endpoint, {
headers: {
// Add auth headers if needed
},
});

export async function fetchGraphQL<T>(
query: string,
variables?: Record<string, unknown>
): Promise<T> {
return client.request<T>(query, variables);
}

/graphql - GraphQL Configuration

GraphQL queries, fragments, and generated types:

graphql/
├── queries/ # Query files
│ ├── pages.graphql
│ ├── posts.graphql
│ └── menus.graphql
├── fragments/ # Reusable fragments
│ ├── post-fields.graphql
│ └── image-fields.graphql
└── generated/ # Auto-generated (don't edit)
├── graphql.ts # Types and operations
└── schema.json # Full schema

Example Query

# graphql/queries/pages.graphql
query GetPageBySlug($slug: ID!) {
page(id: $slug, idType: URI) {
id
title
slug
content
editorBlocks {
...EditorBlockFields
}
featuredImage {
node {
...ImageFields
}
}
}
}

/public - Static Assets

Public files served at root URL:

public/
├── favicon.ico # Site favicon
├── logo.svg # Site logo
├── og-image.jpg # Default social image
└── robots.txt # Search engine rules

Configuration Files

next.config.ts

import type { NextConfig } from 'next';

const config: NextConfig = {
images: {
remotePatterns: [
{
protocol: 'https',
hostname: 'your-wordpress.com',
},
],
},
// Enable experimental features as needed
};

export default config;

tailwind.config.ts

import type { Config } from 'tailwindcss';

export default {
darkMode: ['class'],
content: [
'./app/**/*.{ts,tsx}',
'./components/**/*.{ts,tsx}',
],
theme: {
extend: {
colors: {
// Custom colors
},
},
},
plugins: [
require('@tailwindcss/typography'),
],
} satisfies Config;

codegen.ts

import type { CodegenConfig } from '@graphql-codegen/cli';

const config: CodegenConfig = {
schema: process.env.NEXT_PUBLIC_WORDPRESS_API_URL,
documents: ['graphql/**/*.graphql'],
generates: {
'graphql/generated/graphql.ts': {
plugins: [
'typescript',
'typescript-operations',
'typescript-graphql-request',
],
},
},
};

export default config;

File Naming Conventions

Components

  • PascalCase for component files: PostCard.tsx
  • index.ts for barrel exports: components/ui/index.ts
  • kebab-case for non-component files: use-posts.ts

Routes

  • lowercase folder names: app/blog/
  • page.tsx for page components
  • layout.tsx for layouts
  • route.ts for API routes

GraphQL

  • kebab-case for query files: get-posts.graphql
  • PascalCase for operations: GetPostBySlug

Import Aliases

FlatWP uses path aliases for cleaner imports:

// tsconfig.json
{
"compilerOptions": {
"paths": {
"@/*": ["./*"],
"@/components/*": ["./components/*"],
"@/lib/*": ["./lib/*"],
"@/graphql/*": ["./graphql/*"]
}
}
}

Usage:

// Instead of
import { Button } from '../../../components/ui/button';

// Use
import { Button } from '@/components/ui/button';

Environment Files

.env.example              # Template (commit this)
.env.local # Local development (don't commit)
.env.production # Production values (set in hosting)

Scripts

Available npm scripts in package.json:

ScriptDescription
devStart development server
buildBuild for production
startRun production build
lintRun ESLint
type-checkRun TypeScript checks
graphql:codegenGenerate GraphQL types
setupInteractive environment setup
download-pluginDownload FlatWP plugin

See Also