Skip to content

Commit a1f6568

Browse files
committed
docs: implement nextjs example
1 parent 37ebe3f commit a1f6568

21 files changed

Lines changed: 1103 additions & 144 deletions

File tree

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"extends": "next/core-web-vitals"
3+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
node_modules
2+
.next
3+
.env.local
4+
.DS_Store
5+
*.log
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
# @faustjs/nextjs Template Hierarchy Example
2+
3+
This example demonstrates how to use the `@faustjs/nextjs` package to automatically discover WordPress templates in a Next.js project using the WordPress template hierarchy.
4+
5+
## What This Example Shows
6+
7+
- **Template Discovery**: Automatically finds WordPress template files in your `wp-templates` directory
8+
- **Template Registry**: Uses dynamic imports to organize and load templates efficiently
9+
- **Template Hierarchy**: Follows WordPress template hierarchy rules for organizing templates
10+
- **Template Resolution**: SSR catch-all route that dynamically resolves WordPress URLs to templates
11+
12+
## Project Structure
13+
14+
```
15+
src/
16+
├── components/
17+
│ └── WordPressLayout.js # Shared layout for all templates
18+
├── pages/
19+
│ ├── _app.js # Next.js app wrapper
20+
│ ├── _document.js # Next.js document structure
21+
│ ├── index.js # Demo page showing discovered templates
22+
│ ├── 404.js # Custom 404 page
23+
│ └── [...uri].js # Catch-all route implementing template resolution
24+
├── styles/
25+
│ └── globals.css # Global styles
26+
└── wp-templates/
27+
├── index.js # Template registry with dynamic imports
28+
├── page.js # Page template
29+
├── single.js # Single post template
30+
├── archive.js # Archive template
31+
└── index-template.js # Index/fallback template
32+
```
33+
34+
## How It Works
35+
36+
1. **Template Files**: Place your WordPress templates in `src/wp-templates/`
37+
2. **Template Registry**: The `index.js` file exports all templates with dynamic imports
38+
3. **Next.js Integration**: Uses Next.js dynamic imports for optimal loading
39+
4. **SSR Resolution**: The `[...uri].js` route dynamically fetches content and resolves templates
40+
5. **Shared Layout**: All templates use `WordPressLayout.js` for consistent styling
41+
6. **Path Aliases**: Uses `@/` alias for clean imports (configured in `jsconfig.json` and `next.config.js`)
42+
43+
## Available Templates
44+
45+
The following templates are currently registered and available:
46+
47+
- **single.js** - Displays individual blog posts
48+
- **page.js** - Displays individual WordPress pages
49+
- **archive.js** - Displays category, tag, and other archive pages
50+
- **index-template.js** - Fallback template for any content without a specific template
51+
52+
## Running the Example
53+
54+
```bash
55+
# Install dependencies
56+
pnpm install
57+
58+
# Copy environment configuration
59+
cp .env.example .env.local
60+
61+
# Edit .env.local to set your WordPress URL
62+
# WORDPRESS_URL=https://your-wordpress-site.com
63+
64+
# Start the development server
65+
pnpm dev
66+
```
67+
68+
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
69+
70+
## Template Discovery
71+
72+
The example shows discovered templates from the `wp-templates` directory on the home page, displaying:
73+
74+
- Template name and type
75+
- File path information
76+
- Template descriptions
77+
78+
The templates are loaded dynamically using Next.js dynamic imports for optimal performance.
79+
80+
## Live Template Router Demo
81+
82+
Try these example URLs to see the template hierarchy in action:
83+
84+
- `/about` → Resolves to `page.js` template
85+
- `/hello-world` → Resolves to `single.js` template
86+
- `/category/uncategorized` → Resolves to `archive.js` template
87+
- `/tag/fun` → Resolves to `archive.js` template
88+
89+
## Environment Variables
90+
91+
Create a `.env.local` file with:
92+
93+
```bash
94+
WORDPRESS_URL=https://your-wordpress-site.com
95+
```
96+
97+
Replace with your actual WordPress site URL.
98+
99+
## Learn More
100+
101+
- [Next.js Documentation](https://nextjs.org/docs) - Learn about Next.js features and API
102+
- [@faustjs/nextjs Documentation](../../packages/nextjs/README.md) - Learn about the template hierarchy system
103+
- [@faustjs/template-hierarchy Documentation](../../packages/template-hierarchy/README.md) - Understand the template hierarchy implementation
104+
105+
## Dependencies
106+
107+
This example uses:
108+
109+
- **@faustjs/nextjs** - Next.js integration for Faust.js
110+
- **@faustjs/template-hierarchy** - Template hierarchy system
111+
- **Next.js 15** - React framework
112+
- **GraphQL** - Query language for APIs
113+
- **React 18** - UI library
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"compilerOptions": {
3+
"baseUrl": ".",
4+
"paths": {
5+
"@/*": ["./src/*"]
6+
}
7+
},
8+
"exclude": ["node_modules"]
9+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
const path = require('path');
2+
3+
/** @type {import('next').NextConfig} */
4+
const nextConfig = {
5+
reactStrictMode: true,
6+
experimental: {
7+
esmExternals: true,
8+
},
9+
webpack: (config) => {
10+
config.resolve.alias = {
11+
...config.resolve.alias,
12+
'@': path.resolve(__dirname, 'src'),
13+
};
14+
return config;
15+
},
16+
};
17+
18+
module.exports = nextConfig;
Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
{
22
"name": "@faustjs/nextjs-template-hierarchy-example",
3-
"version": "1.0.0",
43
"private": true,
4+
"version": "0.1.0",
5+
"license": "0BSD",
56
"description": "Example Next.js app using @faustjs/nextjs template hierarchy",
67
"scripts": {
78
"dev": "next dev",
@@ -12,12 +13,17 @@
1213
"dependencies": {
1314
"@faustjs/nextjs": "workspace:*",
1415
"@faustjs/template-hierarchy": "workspace:*",
15-
"next": "^14.0.0",
16-
"react": "^18.0.0",
17-
"react-dom": "^18.0.0"
16+
"graphql": "^16.9.0",
17+
"graphql-tag": "^2.12.6",
18+
"next": "^15.1.3",
19+
"react": "^18.3.1",
20+
"react-dom": "^18.3.1"
1821
},
1922
"devDependencies": {
20-
"eslint": "^8.0.0",
21-
"eslint-config-next": "^14.0.0"
23+
"eslint": "^8.57.0",
24+
"eslint-config-next": "^15.1.3"
25+
},
26+
"engines": {
27+
"node": ">=24"
2228
}
2329
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import Head from 'next/head';
2+
3+
export default function WordPressLayout({ title, children }) {
4+
return (
5+
<>
6+
<Head>
7+
<title>{title}</title>
8+
</Head>
9+
<main>{children}</main>
10+
</>
11+
);
12+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import Head from 'next/head';
2+
3+
export default function Custom404() {
4+
return (
5+
<>
6+
<Head>
7+
<title>404 - Page Not Found</title>
8+
</Head>
9+
<main style={{ textAlign: 'center', padding: '40px 20px' }}>
10+
<h1 style={{ color: '#dc2626' }}>404 - Page Not Found</h1>
11+
<p>Sorry, the page you are looking for could not be found.</p>
12+
<p>
13+
This might happen if the WordPress content doesn't exist or if there's
14+
no matching template for the requested URI.
15+
</p>
16+
<a href="/" style={{ color: '#3b82f6', fontWeight: '500' }}>
17+
← Back to Home
18+
</a>
19+
</main>
20+
</>
21+
);
22+
}
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
import {
2+
uriToTemplate,
3+
setGraphQLClient,
4+
createDefaultClient,
5+
} from '@faustjs/nextjs/pages';
6+
import availableTemplates from '@/wp-templates';
7+
8+
export default function Page(props) {
9+
const { templateData } = props;
10+
11+
if (!templateData?.template?.id) {
12+
return (
13+
<div style={{ padding: '20px', textAlign: 'center' }}>
14+
<h1 style={{ color: 'red' }}>Template not found</h1>
15+
<p>No template could be resolved for this URI.</p>
16+
</div>
17+
);
18+
}
19+
20+
const PageTemplate = availableTemplates[templateData.template.id];
21+
22+
if (!PageTemplate) {
23+
return (
24+
<div style={{ padding: '20px', textAlign: 'center' }}>
25+
<h1 style={{ color: 'red' }}>Component not found</h1>
26+
<p>Template "{templateData.template.id}" is not available.</p>
27+
<pre
28+
style={{ textAlign: 'left', background: '#f5f5f5', padding: '10px' }}>
29+
{JSON.stringify(templateData, null, 2)}
30+
</pre>
31+
</div>
32+
);
33+
}
34+
35+
return <PageTemplate {...props} />;
36+
}
37+
38+
export async function getServerSideProps(context) {
39+
const { params } = context;
40+
41+
const uri = Array.isArray(params?.uri)
42+
? '/' + params.uri.join('/') + '/'
43+
: '/';
44+
45+
const client = createDefaultClient(process.env.WORDPRESS_URL);
46+
setGraphQLClient(client);
47+
48+
try {
49+
const templateData = await uriToTemplate({
50+
uri,
51+
availableTemplates: Object.keys(availableTemplates),
52+
wordpressUrl: process.env.WORDPRESS_URL,
53+
});
54+
55+
if (
56+
!templateData?.template?.id ||
57+
templateData?.template?.id === '404 Not Found'
58+
) {
59+
return { notFound: true };
60+
}
61+
62+
return {
63+
props: {
64+
uri,
65+
templateData: JSON.parse(JSON.stringify(templateData)),
66+
},
67+
};
68+
} catch (error) {
69+
console.error('Error resolving template:', error);
70+
return { notFound: true };
71+
}
72+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import '../styles/globals.css';
2+
3+
export default function App({ Component, pageProps }) {
4+
return <Component {...pageProps} />;
5+
}

0 commit comments

Comments
 (0)