Skip to content

Commit 4056de6

Browse files
authored
Next template hierarchy Sveltekit example (#2153)
* sveltekit template-hierarchy implementation * add page template
1 parent 4508dcb commit 4056de6

20 files changed

Lines changed: 658 additions & 24 deletions

File tree

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
node_modules
2+
3+
# Output
4+
.output
5+
.vercel
6+
.netlify
7+
.wrangler
8+
/.svelte-kit
9+
/build
10+
11+
# OS
12+
.DS_Store
13+
Thumbs.db
14+
15+
# Env
16+
.env
17+
.env.*
18+
!.env.example
19+
!.env.test
20+
21+
# Vite
22+
vite.config.js.timestamp-*
23+
vite.config.ts.timestamp-*
24+
25+
package-lock.json
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
# FaustJS SvelteKit Template Hierarchy Example
2+
3+
This example demonstrates how to use FaustJS with SvelteKit to create a headless WordPress application with automatic template hierarchy support.
4+
5+
## What is Template Hierarchy?
6+
7+
WordPress template hierarchy determines which template file is used to display different types of content (posts, pages, archives, etc.). This example shows how to implement similar functionality in a SvelteKit application using FaustJS.
8+
9+
## Features
10+
11+
- ✅ Automatic template selection based on WordPress content type
12+
- ✅ Support for custom post types and archives
13+
- ✅ WordPress-style template hierarchy (single.svelte, archive.svelte, index.svelte)
14+
- ✅ GraphQL data fetching with URQL
15+
- ✅ Server-side rendering (SSR)
16+
17+
## Getting Started
18+
19+
### Prerequisites
20+
21+
- Node.js v16.0.0 or newer
22+
- A WordPress site with the [FaustJS plugin](https://wordpress.org/plugins/faustwp/) installed
23+
- WPGraphQL plugin installed on your WordPress site
24+
25+
### Installation
26+
27+
1. Clone this repository or copy this example
28+
2. Install dependencies:
29+
30+
```bash
31+
npm install
32+
```
33+
34+
3. Configure your WordPress URL in the `.env` file:
35+
36+
```bash
37+
WORDPRESS_URL=https://your-wordpress-site.com
38+
```
39+
40+
### Development
41+
42+
Start the development server:
43+
44+
```bash
45+
npm run dev
46+
47+
# or start the server and open the app in a new browser tab
48+
npm run dev -- --open
49+
```
50+
51+
The application will automatically:
52+
53+
- Fetch content from your WordPress site
54+
- Determine the appropriate template based on the URL
55+
- Render the content using the matching Svelte template
56+
57+
### Template Structure
58+
59+
Templates are located in `src/wp-templates/`:
60+
61+
- `index.svelte` - Default template (homepage, fallback)
62+
- `single.svelte` - Single post/page template
63+
- `archive.svelte` - Archive pages (categories, tags, custom post types)
64+
65+
### Building
66+
67+
To create a production version of your app:
68+
69+
```bash
70+
npm run build
71+
```
72+
73+
You can preview the production build with `npm run preview`.
74+
75+
## How It Works
76+
77+
1. The `[...uri]/+page.server.js` route catches all URLs
78+
2. Uses `uriToTemplate()` from `@faustjs/sveltekit` to:
79+
- Query WordPress for content at the given URI
80+
- Determine the appropriate template type
81+
- Fetch the necessary data
82+
3. Renders the content using the matching Svelte template
83+
84+
## Learn More
85+
86+
- [FaustJS Documentation](https://faustjs.org/docs/)
87+
- [SvelteKit Documentation](https://kit.svelte.dev/docs)
88+
- [WordPress Template Hierarchy](https://developer.wordpress.org/themes/basics/template-hierarchy/)
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
{
2+
"name": "@faustjs/sveltekit-template-hierarchy-example",
3+
"private": true,
4+
"version": "0.1.0",
5+
"license": "0BSD",
6+
"type": "module",
7+
"scripts": {
8+
"dev": "vite dev",
9+
"build": "vite build",
10+
"preview": "vite preview",
11+
"prepare": "svelte-kit sync || echo ''",
12+
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
13+
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch"
14+
},
15+
"devDependencies": {
16+
"@faustjs/sveltekit": "workspace:*",
17+
"@faustjs/template-hierarchy": "workspace:*",
18+
"@sveltejs/adapter-auto": "^6.0.0",
19+
"@sveltejs/kit": "^2.16.0",
20+
"@sveltejs/vite-plugin-svelte": "^5.0.0",
21+
"svelte": "^5.0.0",
22+
"svelte-check": "^4.0.0",
23+
"vite": "^6.2.6"
24+
},
25+
"dependencies": {
26+
"@urql/core": "^5.1.1",
27+
"@urql/exchange-persisted": "^4.3.1",
28+
"deepmerge": "^4.3.1",
29+
"graphql": "^16.11.0"
30+
}
31+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<!doctype html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="utf-8" />
5+
<link rel="icon" href="%sveltekit.assets%/favicon.png" />
6+
<meta name="viewport" content="width=device-width, initial-scale=1" />
7+
%sveltekit.head%
8+
</head>
9+
<body data-sveltekit-preload-data="hover">
10+
<div style="display: contents">%sveltekit.body%</div>
11+
</body>
12+
</html>
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import { dev } from '$app/environment';
2+
3+
export const handle = async ({ event, resolve }) => {
4+
if (
5+
dev &&
6+
event.url.pathname === '/.well-known/appspecific/com.chrome.devtools.json'
7+
) {
8+
return new Response(undefined, { status: 404 });
9+
}
10+
11+
return resolve(event, {
12+
filterSerializedResponseHeaders: () => true, // basically get all headers
13+
});
14+
};
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<script>
2+
const { children } = $props();
3+
</script>
4+
5+
<main>
6+
{@render children()}
7+
</main>
8+
9+
<style>
10+
main {
11+
background: white;
12+
padding: 20px;
13+
border-radius: 8px;
14+
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
15+
max-width: 800px;
16+
margin: 0 auto;
17+
}
18+
</style>
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
export const load = async (event) => {
2+
const { data } = event;
3+
4+
const template = await import(`$wp/${data.templateData.template.id}.svelte`);
5+
6+
return {
7+
...data,
8+
template: template.default,
9+
};
10+
};
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import {
2+
createDefaultClient,
3+
setGraphQLClient,
4+
uriToTemplate,
5+
} from '@faustjs/sveltekit';
6+
import { WORDPRESS_URL } from '$env/static/private';
7+
8+
export const load = async (event) => {
9+
const {
10+
params: { uri },
11+
fetch,
12+
} = event;
13+
14+
const workingUri = uri || '/';
15+
16+
const client = createDefaultClient(WORDPRESS_URL);
17+
setGraphQLClient(client);
18+
19+
const templateData = await uriToTemplate({
20+
fetch,
21+
uri: workingUri,
22+
graphqlClient: client,
23+
});
24+
25+
return {
26+
uri: workingUri,
27+
templateData,
28+
};
29+
};
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<script>
2+
const { data } = $props();
3+
</script>
4+
5+
<data.template data={data.graphqlData} />
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import { readdir } from 'node:fs/promises';
2+
import { join } from 'node:path';
3+
import { json } from '@sveltejs/kit';
4+
const TEMPLATE_PATH = 'wp-templates';
5+
6+
export const GET = async ({ url }) => {
7+
const uri = url.searchParams.get('uri');
8+
9+
if (!uri) {
10+
return new Response('Missing URI', { status: 400 });
11+
}
12+
13+
const files = await readdir(join('src', TEMPLATE_PATH));
14+
15+
const templates = [];
16+
17+
for (const file of files) {
18+
if (file.startsWith('+')) {
19+
continue;
20+
}
21+
22+
const slug = file.replace('.svelte', '');
23+
24+
templates.push({
25+
id: slug,
26+
path: join('/', TEMPLATE_PATH, slug),
27+
});
28+
}
29+
30+
return json(templates);
31+
};

0 commit comments

Comments
 (0)