eddev 2.3.12 → 2.3.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (95) hide show
  1. package/dist/app/entry/MetaTags.d.ts.map +1 -1
  2. package/dist/app/entry/MetaTags.js +2 -0
  3. package/dist/app/lib/blocks/block-utils.d.ts +2 -2
  4. package/dist/app/lib/blocks/block-utils.d.ts.map +1 -1
  5. package/dist/app/lib/blocks/block-utils.js +10 -3
  6. package/dist/app/lib/blocks/editor/root-blocks.d.ts.map +1 -1
  7. package/dist/app/lib/blocks/editor/root-blocks.js +2 -2
  8. package/dist/app/lib/devtools/hooks/useTailwind.d.ts +45 -45
  9. package/dist/app/lib/hooks/query-hooks.d.ts +8 -8
  10. package/dist/app/lib/hooks/query-hooks.d.ts.map +1 -1
  11. package/dist/app/lib/hooks/query-hooks.js +2 -1
  12. package/dist/app/lib/runtime/apiConfig.d.ts +15 -0
  13. package/dist/app/lib/runtime/apiConfig.d.ts.map +1 -1
  14. package/dist/app/server/proxy-wp-admin.d.ts.map +1 -1
  15. package/dist/app/server/proxy-wp-admin.js +1 -0
  16. package/dist/app/server/server-context.d.ts +2 -2
  17. package/dist/app/server/server-context.d.ts.map +1 -1
  18. package/dist/app/utils/trpc-client.d.ts.map +1 -1
  19. package/dist/app/utils/trpc-client.js +19 -3
  20. package/dist/node/cli/version.d.ts +1 -1
  21. package/dist/node/cli/version.js +1 -1
  22. package/dist/node/compiler/dev-server.js +1 -1
  23. package/dist/node/compiler/get-vite-config.d.ts.map +1 -1
  24. package/dist/node/compiler/get-vite-config.js +1 -0
  25. package/dist/node/compiler/vinxi-app.d.ts +1 -1
  26. package/dist/node/compiler/vinxi-app.d.ts.map +1 -1
  27. package/dist/node/utils/fetch-wp.d.ts.map +1 -1
  28. package/dist/node/utils/fetch-wp.js +2 -1
  29. package/dist/node/utils/fs.d.ts +22 -19
  30. package/dist/node/utils/fs.d.ts.map +1 -1
  31. package/package.json +2 -2
  32. package/skills/eddev/SKILL.md +156 -0
  33. package/skills/eddev/docs/acf/admin-panel-widgets.mdx +99 -0
  34. package/skills/eddev/docs/acf/custom-enums.mdx +75 -0
  35. package/skills/eddev/docs/acf/custom-fields.mdx +131 -0
  36. package/skills/eddev/docs/acf.mdx +31 -0
  37. package/skills/eddev/docs/blocks/block-definition.mdx +189 -0
  38. package/skills/eddev/docs/blocks/core-blocks.mdx +86 -0
  39. package/skills/eddev/docs/blocks/data-and-editing.mdx +219 -0
  40. package/skills/eddev/docs/blocks/editor-config.mdx +157 -0
  41. package/skills/eddev/docs/blocks/nested-blocks.mdx +129 -0
  42. package/skills/eddev/docs/blocks/overview.mdx +58 -0
  43. package/skills/eddev/docs/blocks/template-parts.mdx +131 -0
  44. package/skills/eddev/docs/config.mdx +200 -0
  45. package/skills/eddev/docs/design/color.mdx +185 -0
  46. package/skills/eddev/docs/design/favicons.mdx +103 -0
  47. package/skills/eddev/docs/design/grid.mdx +120 -0
  48. package/skills/eddev/docs/design/icons.mdx +197 -0
  49. package/skills/eddev/docs/design/responsive-scaling.mdx +312 -0
  50. package/skills/eddev/docs/design/type.mdx +125 -0
  51. package/skills/eddev/docs/devtool/cli.mdx +201 -0
  52. package/skills/eddev/docs/devtool/overlay.mdx +5 -0
  53. package/skills/eddev/docs/getting-started.mdx +53 -0
  54. package/skills/eddev/docs/graphql/extending.mdx +186 -0
  55. package/skills/eddev/docs/graphql/fragments.mdx +107 -0
  56. package/skills/eddev/docs/graphql/global-data.mdx +47 -0
  57. package/skills/eddev/docs/graphql/infinite-queries.mdx +95 -0
  58. package/skills/eddev/docs/graphql/mutation-hooks.mdx +111 -0
  59. package/skills/eddev/docs/graphql/query-hooks.mdx +122 -0
  60. package/skills/eddev/docs/graphql/tooling.mdx +50 -0
  61. package/skills/eddev/docs/graphql.mdx +97 -0
  62. package/skills/eddev/docs/guides/color-schemes.mdx +204 -0
  63. package/skills/eddev/docs/guides/integrations.mdx +3 -0
  64. package/skills/eddev/docs/guides/page-transitions.mdx +5 -0
  65. package/skills/eddev/docs/guides/seo.mdx +5 -0
  66. package/skills/eddev/docs/guides/state-management.mdx +5 -0
  67. package/skills/eddev/docs/infra/caching.mdx +9 -0
  68. package/skills/eddev/docs/infra/deployment.mdx +13 -0
  69. package/skills/eddev/docs/infra/local.mdx +5 -0
  70. package/skills/eddev/docs/infra/security.mdx +11 -0
  71. package/skills/eddev/docs/routing/api.mdx +731 -0
  72. package/skills/eddev/docs/routing/custom.mdx +123 -0
  73. package/skills/eddev/docs/routing/full-details.mdx +37 -0
  74. package/skills/eddev/docs/routing/wordpress.mdx +70 -0
  75. package/skills/eddev/docs/routing.mdx +18 -0
  76. package/skills/eddev/docs/serverless/functions.mdx +436 -0
  77. package/skills/eddev/docs/serverless.mdx +202 -0
  78. package/skills/eddev/docs/snippets/automated-block-layouts.mdx +97 -0
  79. package/skills/eddev/docs/snippets/custom-routes-and-urls.mdx +91 -0
  80. package/skills/eddev/docs/snippets/multiple-editable-zones.mdx +87 -0
  81. package/skills/eddev/docs/snippets/querying-specific-blocks.mdx +164 -0
  82. package/skills/eddev/docs/snippets/submitting-forms-to-rpc.mdx +91 -0
  83. package/skills/eddev/docs/snippets/svgs.mdx +38 -0
  84. package/skills/eddev/docs/snippets/type-safe-acf-dropdowns.mdx +72 -0
  85. package/skills/eddev/docs/snippets.mdx +19 -0
  86. package/skills/eddev/docs/software.mdx +19 -0
  87. package/skills/eddev/docs/stack/how-it-works.mdx +50 -0
  88. package/skills/eddev/docs/stack/overview.mdx +56 -0
  89. package/skills/eddev/docs/stack/spa-vs-ssr.mdx +52 -0
  90. package/skills/eddev/docs/views/app-view.mdx +97 -0
  91. package/skills/eddev/docs/views/page-templates.mdx +82 -0
  92. package/skills/eddev/docs/views/queries.mdx +116 -0
  93. package/skills/eddev/docs/views.mdx +63 -0
  94. package/skills/eddev/index.mdx +79 -0
  95. package/tsconfig.app.json +2 -2
@@ -0,0 +1,91 @@
1
+ # Submitting Forms To RPC (/docs/snippets/submitting-forms-to-rpc)
2
+
3
+ **Create a serverless mutation and call it from React**
4
+
5
+ Use this when the frontend needs to submit to a third-party API or perform server-side work without exposing credentials in the browser.
6
+
7
+ For the full API reference, see [RPC Functions](/docs/serverless/functions). For generated GraphQL mutations, see [Mutation Hooks](/docs/graphql/mutation-hooks).
8
+
9
+ ## Create A Mutation Route [#create-a-mutation-route]
10
+
11
+ ```ts filename="server/routes/api.newsletter.ts"
12
+ import { rpc } from "eddev/server"
13
+ import { z } from "zod"
14
+
15
+ export default rpc.router({
16
+ signup: rpc.procedure
17
+ .input(
18
+ z.object({
19
+ email: z.string().email(),
20
+ lists: z.array(
21
+ z.object({
22
+ listId: z.string(),
23
+ title: z.string(),
24
+ }),
25
+ ),
26
+ }),
27
+ )
28
+ .mutation(async ({ input }) => {
29
+ const results = await Promise.all(
30
+ input.lists.map(async (list) => {
31
+ const params = new URLSearchParams()
32
+ params.append("EMAIL", input.email)
33
+ params.append("id", list.listId)
34
+
35
+ const response = await fetch(`https://example.com/subscribe?${params}`, {
36
+ method: "GET",
37
+ })
38
+
39
+ return response.ok
40
+ }),
41
+ )
42
+
43
+ if (results.every((ok) => !ok)) {
44
+ throw new Error("Failed to subscribe")
45
+ }
46
+
47
+ return { success: true }
48
+ }),
49
+ })
50
+ ```
51
+
52
+ ## Call It From React [#call-it-from-react]
53
+
54
+ ```tsx filename="features/newsletter/NewsletterForm.tsx"
55
+ import { useRPCMutation } from "eddev/hooks"
56
+ import { useState } from "react"
57
+
58
+ export function NewsletterForm() {
59
+ const [email, setEmail] = useState("")
60
+ const signup = useRPCMutation("api.newsletter.signup")
61
+
62
+ if (signup.isSuccess) {
63
+ return <p>Thank you for signing up.</p>
64
+ }
65
+
66
+ return (
67
+ <form
68
+ onSubmit={(event) => {
69
+ event.preventDefault()
70
+ signup.mutate({
71
+ email,
72
+ lists: [{ listId: "abc123", title: "Newsletter" }],
73
+ })
74
+ }}
75
+ >
76
+ {signup.isError && <p>{signup.error.message}</p>}
77
+ <input
78
+ type="email"
79
+ value={email}
80
+ disabled={signup.isPending}
81
+ onChange={(event) => setEmail(event.currentTarget.value)}
82
+ />
83
+ <button type="submit" disabled={signup.isPending}>
84
+ Sign up
85
+ </button>
86
+ </form>
87
+ )
88
+ }
89
+ ```
90
+
91
+ RPC routes are part of the serverless frontend. Make sure `serverless.endpoints` is configured for WordPress-hosted SPA/admin usage. See [Serverless](/docs/serverless) for deployment setup.
@@ -0,0 +1,38 @@
1
+ # SVGs (/docs/snippets/svgs)
2
+
3
+ **Copy/paste SVG upload and GraphQL snippets**
4
+
5
+ ## Expose Raw SVG Content [#expose-raw-svg-content]
6
+
7
+ Use a computed GraphQL field when the frontend needs the raw SVG string instead of a media URL.
8
+
9
+ ```php
10
+ add_action('graphql_register_types', function () {
11
+ register_graphql_field('MediaItem', 'svgContent', [
12
+ 'type' => 'String',
13
+ 'description' => __('The raw SVG content of the media item', 'your-textdomain'),
14
+ 'resolve' => function ($mediaItem) {
15
+ $filePath = get_attached_file($mediaItem->ID);
16
+
17
+ if (preg_match('/\.svg$/i', $filePath) && file_exists($filePath)) {
18
+ return file_get_contents($filePath);
19
+ }
20
+
21
+ return null;
22
+ },
23
+ ]);
24
+ });
25
+ ```
26
+
27
+ ```graphql filename="blocks/content/icon.graphql"
28
+ query IconBlock {
29
+ block {
30
+ content_icon {
31
+ icon {
32
+ svgContent
33
+ altText
34
+ }
35
+ }
36
+ }
37
+ }
38
+ ```
@@ -0,0 +1,72 @@
1
+ # Type-Safe ACF Dropdowns (/docs/snippets/type-safe-acf-dropdowns)
2
+
3
+ **Register one enum field and use its generated TypeScript type**
4
+
5
+ Use this when a CMS dropdown should become a narrow TypeScript type instead of a loose `string`.
6
+
7
+ For the fundamentals, see [Custom Enums](/docs/acf/custom-enums). For broader color-theme usage, see [Color Schemes](/docs/guides/color-schemes). For schema extension context, see [Extending GraphQL](/docs/graphql/extending).
8
+
9
+ ## Register The Enum Field Type [#register-the-enum-field-type]
10
+
11
+ Put enum field registrations in backend field files. For a page color selector, use:
12
+
13
+ ```php filename="backend/fields/page-color.php"
14
+ <?php
15
+
16
+ ED()->registerEnumFieldType("page-color", [
17
+ "label" => "Page Color",
18
+ "type" => "select",
19
+ "allow_null" => 1,
20
+ "options" => [
21
+ "silver" => "Silver",
22
+ "green" => "Green",
23
+ "blue" => "Blue",
24
+ "orange" => "Orange",
25
+ "yellow" => "Yellow",
26
+ ],
27
+ ]);
28
+ ```
29
+
30
+ `type` controls the underlying ACF UI. eddev supports `select`, `button_group`, `radio`, and `checkbox`; use `select` unless the field needs a different editing control. `checkbox` returns multiple values.
31
+
32
+ ## Add It Anywhere ACF Fields Are Used [#add-it-anywhere-acf-fields-are-used]
33
+
34
+ Once registered, `page-color` is available as an ACF field type. Add it to a block field group, post type field group, options page, taxonomy, or any other ACF location the same way you would add a normal ACF field.
35
+
36
+ When that field is selected in GraphQL, codegen uses the enum field name to create a TypeScript type: `page-color` becomes `PageColor`. Current generated GraphQL exports include an `Option` suffix, so the import is `PageColorOption`.
37
+
38
+ ## Use The Generated Type In React [#use-the-generated-type-in-react]
39
+
40
+ Keep the component API simple. Alias the generated type if the component wants the shorter `PageColor` name.
41
+
42
+ ```tsx filename="components/PageColorPanel.tsx"
43
+ import type { PageColorOption } from "@generated-types"
44
+ import type { ReactNode } from "react"
45
+
46
+ type PageColor = PageColorOption
47
+
48
+ type Props = {
49
+ color: PageColor
50
+ title: string
51
+ children: ReactNode
52
+ }
53
+
54
+ const colorClasses: Record<PageColor, string> = {
55
+ silver: "theme-silver",
56
+ green: "theme-green",
57
+ blue: "theme-blue",
58
+ orange: "theme-orange",
59
+ yellow: "theme-yellow",
60
+ }
61
+
62
+ export function PageColorPanel(props: Props) {
63
+ return (
64
+ <section className={colorClasses[props.color]}>
65
+ <h2>{props.title}</h2>
66
+ {props.children}
67
+ </section>
68
+ )
69
+ }
70
+ ```
71
+
72
+ Keep the PHP options and `colorClasses` in sync. If a new CMS option is added, TypeScript will force you to decide how it renders.
@@ -0,0 +1,19 @@
1
+ # Overview (/docs/snippets)
2
+
3
+ **Situation-based recipes from real eddev themes**
4
+
5
+ Snippets are short recipes for specific situations. Use them when you know the problem you are solving and want a source-backed starting point to paste into a theme.
6
+
7
+ These examples are simplified from Sol People and SFF-style project patterns. Each page links back to the deeper docs for the underlying API.
8
+
9
+ | Recipe | Use It When |
10
+ | ------------------------------------------------------ | ----------------------------------------------------------------------------------- |
11
+ | [Querying Specific Blocks](./querying-specific-blocks) | You need one block, a subset of blocks, or blocks from another post/template part. |
12
+ | [Type-Safe ACF Dropdowns](./type-safe-acf-dropdowns) | A CMS dropdown should become a generated TypeScript union. |
13
+ | [Multiple Editable Zones](./multiple-editable-zones) | One block needs more than one independent editable child area. |
14
+ | [Automated Block Layouts](./automated-block-layouts) | Layout wrappers should be driven by block flags instead of repeated in every block. |
15
+ | [Submitting Forms To RPC](./submitting-forms-to-rpc) | A React form needs to call server-side code or a third-party API. |
16
+ | [Custom Routes And URLs](./custom-routes-and-urls) | A fixed URL should map to a view, or generated WordPress links need remapping. |
17
+ | [SVGs](./svgs) | SVG uploads or raw SVG GraphQL fields are needed. |
18
+
19
+ If a pattern already has a focused docs page, this section should link to it instead of duplicating it. For example, reusable GraphQL fragments are covered in [GraphQL Fragments](/docs/graphql/fragments).
@@ -0,0 +1,19 @@
1
+ # Software and Tooling (/docs/software)
2
+
3
+ **Tools we use to build, edit, and run eddev projects.**
4
+
5
+ ## Local [#local]
6
+
7
+ [Local](https://localwp.com/) is how we run WordPress locally on our machines, and connects with Flywheel/WP Engine — allowing us to push and pull entire websites to Staging and Production environments (database and all files).
8
+
9
+ <Cards>
10
+ <Card title="How we use Local" href="/docs/infra/local">
11
+ Run WordPress locally and move site data between environments.
12
+ </Card>
13
+ </Cards>
14
+
15
+ ## VSCode [#vscode]
16
+
17
+ [VSCode](https://code.visualstudio.com/) is the industry-default code editor for web development, and you should use it when working on ED. projects.
18
+
19
+ The starter theme includes a `.vscode/extensions.json` file with a list of recommended plugins — you should ensure these are installed.
@@ -0,0 +1,50 @@
1
+ # How It Works (/docs/stack/how-it-works)
2
+
3
+ **Follow the end-to-end request flow for an eddev page.**
4
+
5
+ Every eddev page starts with WordPress. Even when the public frontend runs on Vercel, WordPress decides which route has been requested and which view should render it.
6
+
7
+ ## Route Resolution [#route-resolution]
8
+
9
+ For a normal page request, WordPress resolves the URL using its template hierarchy. Instead of ending at a PHP template, eddev can resolve that template to a React view such as `front-page`, `page`, `single`, `single-{post-type}`, `archive-{post-type}`, or `404`.
10
+
11
+ After the view is known, the PHP runtime builds route data:
12
+
13
+ 1. It runs the paired view GraphQL file when one exists.
14
+ 2. It runs global app data when the request needs it.
15
+ 3. It adds the matched view name, admin context, metadata, and query debug information when available.
16
+ 4. It returns either JSON route data or an HTML response containing that route data.
17
+
18
+ The JSON shape is the contract between WordPress and the React app. React does not independently decide what URL maps to what content.
19
+
20
+ ## SSR Request Flow [#ssr-request-flow]
21
+
22
+ In the usual production setup, public traffic goes to Vercel and WordPress lives on the WP Engine origin.
23
+
24
+ When Vercel receives a page request:
25
+
26
+ 1. The eddev serverless app asks WordPress for the matching route payload.
27
+ 2. WordPress resolves the route, runs the relevant GraphQL files, and returns the payload.
28
+ 3. The serverless app renders the active React view inside the app shell.
29
+ 4. The browser receives HTML with the page content already rendered.
30
+ 5. React hydrates the page so routing, blocks, query hooks, and interactive components can run in the browser.
31
+
32
+ This keeps WordPress as the content and routing source of truth while giving the public site server-rendered HTML.
33
+
34
+ ## SPA Request Flow [#spa-request-flow]
35
+
36
+ SPA mode is what happens when the browser accesses the WordPress origin directly, such as a Local site or a WP Engine origin frontend.
37
+
38
+ In that mode, WordPress still resolves the same route and builds the same route payload. The difference is that the HTML response is an app shell with the route payload embedded for the browser. The browser loads the frontend bundle, reads the payload, loads the matching React view, and renders the page client-side.
39
+
40
+ SPA mode is useful for origin previews and WordPress-hosted fallback behavior, but it is not the preferred public production path for current projects.
41
+
42
+ ## Client Navigation And Data [#client-navigation-and-data]
43
+
44
+ After the first page load, eddev navigation fetches route payloads instead of full HTML pages.
45
+
46
+ In serverless mode, browser navigation asks the JavaScript frontend for route data, and the serverless app fetches or caches the matching WordPress payload. In WordPress-hosted SPA mode, the browser asks WordPress for the route payload directly.
47
+
48
+ Runtime GraphQL hooks use named data endpoints rather than exposing arbitrary GraphQL text from the browser. View, block, and app GraphQL files are part of route rendering; runtime query and mutation hooks are for browser-triggered data after the page is running.
49
+
50
+ For the details behind each part of this flow, see [Views](/docs/views), [Routing](/docs/routing), [GraphQL](/docs/graphql), and [Serverless](/docs/serverless).
@@ -0,0 +1,56 @@
1
+ # Overview (/docs/stack/overview)
2
+
3
+ **Understand the main technologies and hosting shape behind eddev sites.**
4
+
5
+ eddev is ED.'s WordPress + React/TypeScript stack for building content-managed websites. WordPress remains the CMS and routing authority, while the theme is mostly authored as React views, React blocks, GraphQL files, and TypeScript server routes.
6
+
7
+ The usual production shape is a WordPress origin on WP Engine and a public server-rendered frontend on Vercel. Vercel renders the public site, but it still asks WordPress what each URL means and what data should be shown.
8
+
9
+ ## Core Pieces [#core-pieces]
10
+
11
+ | Piece | Role In The Stack |
12
+ | --------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
13
+ | WordPress and PHP | Owns routing, admin, content editing, custom post types, ACF fields, REST endpoints, and the route payload generated for each request. |
14
+ | React and TypeScript | Author the frontend views, blocks, app shell, interactive components, and project-specific server routes. |
15
+ | GraphQL and WPGraphQL | Select exactly what WordPress data is exposed to views, blocks, global app data, and runtime query hooks. |
16
+ | Composer | Installs the PHP-side WordPress dependencies used by the theme, including WPGraphQL packages in the starter theme. |
17
+ | eddev CLI | Discovers views, blocks, routes, and GraphQL files, generates TypeScript outputs, runs local dev servers, and builds SPA or serverless output. |
18
+ | Local | Runs the WordPress origin during local development. |
19
+ | WP Engine | Hosts the WordPress origin for staging and production. |
20
+ | Vercel | Hosts the public JavaScript frontend and serverless routes for normal production traffic. |
21
+
22
+ ## How The Pieces Fit [#how-the-pieces-fit]
23
+
24
+ When someone visits a page, WordPress resolves the URL through the normal template hierarchy. eddev maps that result to a React view in `views/`, runs the paired GraphQL file if one exists, and builds a payload containing the matched view, view data, app data, metadata, and admin context.
25
+
26
+ In SSR mode, the JavaScript server receives the public request, asks WordPress for that payload, renders the matching React view to HTML, and sends the hydrated page to the browser. In SPA mode, WordPress serves the app shell and page payload directly, then the browser renders the React view.
27
+
28
+ Blocks follow the same split. WordPress and ACF own the editor data, block GraphQL files select the public props, and React block components render the frontend output.
29
+
30
+ ## Where To Go Next [#where-to-go-next]
31
+
32
+ <Cards>
33
+ <Card title="How It Works" href="/docs/stack/how-it-works">
34
+ Follow the request and data flow from URL to rendered page.
35
+ </Card>
36
+
37
+ <Card title="SPA vs SSR" href="/docs/stack/spa-vs-ssr">
38
+ Compare WordPress-hosted SPA mode with serverless SSR mode.
39
+ </Card>
40
+
41
+ <Card title="Views" href="/docs/views">
42
+ Learn how WordPress templates map to React views.
43
+ </Card>
44
+
45
+ <Card title="GraphQL" href="/docs/graphql">
46
+ Learn where data queries live and what generated files they produce.
47
+ </Card>
48
+
49
+ <Card title="Serverless" href="/docs/serverless">
50
+ Set up the Vercel frontend in front of the WordPress origin.
51
+ </Card>
52
+
53
+ <Card title="eddev CLI" href="/docs/devtool/cli">
54
+ Run local development, builds, codegen, and diagnostics.
55
+ </Card>
56
+ </Cards>
@@ -0,0 +1,52 @@
1
+ # SPA vs SSR (/docs/stack/spa-vs-ssr)
2
+
3
+ **Compare WordPress-hosted SPA mode with serverless SSR mode.**
4
+
5
+ eddev can render the same site in two modes. The route authority does not change: WordPress still decides what each URL means. The difference is where the React view is rendered for the first response.
6
+
7
+ ## Quick Comparison [#quick-comparison]
8
+
9
+ | Topic | SPA Mode | SSR Mode |
10
+ | ------------------------ | -------------------------------------------------------------------------- | -------------------------------------------------------------------- |
11
+ | First request handled by | WordPress origin, such as Local or WP Engine | JavaScript server, usually Vercel in production |
12
+ | Initial HTML | App shell plus embedded route data | Server-rendered page HTML plus hydration data |
13
+ | React rendering | Browser renders the page after JavaScript loads | Server renders first, browser hydrates after load |
14
+ | WordPress role | CMS, admin, routing, GraphQL execution, route payloads | Same role: CMS, admin, routing, GraphQL execution, route payloads |
15
+ | Best use | Origin previews, fallback behavior, direct Local/WP Engine frontend checks | Normal public frontend and day-to-day local development |
16
+ | Production host | WP Engine can serve it directly, but this is not the preferred public path | Vercel serves the public site while WP Engine remains the CMS origin |
17
+
18
+ ## Local Development [#local-development]
19
+
20
+ When working locally with Local, the two modes usually appear as two nearby URLs:
21
+
22
+ | URL | What You Are Seeing |
23
+ | ---------------------------- | --------------------------------------------------------------------------- |
24
+ | `https://my-site.local` | The WordPress-hosted SPA frontend from Local. |
25
+ | `https://my-site.local:8080` | The local eddev serverless dev server, using Local as the WordPress origin. |
26
+
27
+ Use the `:8080` serverless dev URL for normal frontend work. It is closer to the Vercel production path and catches SSR-specific issues earlier.
28
+
29
+ The direct `.local` URL is still useful when you need to inspect the WordPress origin, confirm the fallback SPA bundle, or work with admin/editor behavior that depends on WordPress-hosted assets.
30
+
31
+ ## Production Hosting [#production-hosting]
32
+
33
+ The common production setup is:
34
+
35
+ | Host | Purpose |
36
+ | -------------------------------------------- | ------------------------------------------------------------------------------------------------- |
37
+ | WP Engine origin, often on a `cms.` hostname | WordPress admin, content editing, route payloads, GraphQL, uploads, REST endpoints, and previews. |
38
+ | Vercel public frontend | Server-rendered public pages, client navigation, serverless routes, and public domains. |
39
+
40
+ Vercel is not replacing WordPress routing. It renders the public response after asking WordPress what the route is and what data belongs to it.
41
+
42
+ ## Build Paths [#build-paths]
43
+
44
+ SPA and SSR also have different build paths:
45
+
46
+ | Build Path | Output |
47
+ | -------------------------- | -------------------------------------------------------------------- |
48
+ | `eddev build` | WordPress-hosted frontend and admin bundles for SPA mode. |
49
+ | `eddev build --serverless` | Serverless output for SSR mode. Vercel uses this path automatically. |
50
+ | `eddev dev --fast` | Local GraphQL codegen plus the serverless dev server on port `8080`. |
51
+
52
+ For command details, see [eddev CLI](/docs/devtool/cli). For deployment configuration, `SITE_URL`, `SITE_API_KEY`, and origin protection, see [Serverless](/docs/serverless).
@@ -0,0 +1,97 @@
1
+ # _app.tsx Layout (/docs/views/app-view)
2
+
3
+ **Common site layout**
4
+
5
+ `views/_app.tsx` is the site shell. It wraps every active view, so it is the right place for global providers, headers, footers, scroll restoration, tracking systems, and route-level UI.
6
+
7
+ ```tsx filename="views/_app.tsx"
8
+ import { Footer } from "@components/site/Footer"
9
+ import { Header } from "@components/site/Header"
10
+ import { ScrollRestoration } from "eddev/routing"
11
+ import { defineView } from "eddev/views"
12
+
13
+ export default defineView("_app", (props) => {
14
+ return (
15
+ <>
16
+ <ScrollRestoration />
17
+ <Header />
18
+ {props.children}
19
+ <Footer />
20
+ </>
21
+ )
22
+ })
23
+ ```
24
+
25
+ The `children` prop is the current route view. Do not query the current page in `_app.tsx`; that belongs in the view's own `.graphql` file.
26
+
27
+ ## App Data [#app-data]
28
+
29
+ Add `views/_app.graphql` for data needed across the whole frontend, such as menus, settings, trackers, or template parts.
30
+
31
+ ```graphql filename="views/_app.graphql"
32
+ query CommonData {
33
+ menus {
34
+ nodes {
35
+ locations
36
+ menuItems {
37
+ nodes {
38
+ label
39
+ url
40
+ parentId
41
+ }
42
+ }
43
+ }
44
+ }
45
+ templateParts {
46
+ siteFooter {
47
+ contentBlocks
48
+ }
49
+ }
50
+ }
51
+ ```
52
+
53
+ The result is available in `_app.tsx` as props, and also through `useAppData()` anywhere inside the app.
54
+
55
+ ```tsx filename="features/site/Footer.tsx"
56
+ import { ContentBlocks } from "eddev/blocks"
57
+ import { useAppData } from "eddev/hooks"
58
+
59
+ export function Footer() {
60
+ const footer = useAppData((data) => data.templateParts?.siteFooter)
61
+
62
+ return <ContentBlocks blocks={footer?.contentBlocks} />
63
+ }
64
+ ```
65
+
66
+ On the initial page request, app data is included with the route payload. During normal client-side navigation it is reused, so avoid putting per-page data in `_app.graphql`.
67
+
68
+ ## Manual Route Display [#manual-route-display]
69
+
70
+ Most projects only need `{props.children}`. More complex sites can render routes manually with `RouteDisplay` when they need page transitions, modal routes, drawers, or background routes.
71
+
72
+ ```tsx filename="views/_app.tsx"
73
+ import { Footer } from "@components/site/Footer"
74
+ import { Header } from "@components/site/Header"
75
+ import { RouteDisplay, useRoute } from "eddev/routing"
76
+ import { defineView } from "eddev/views"
77
+
78
+ export default defineView("_app", () => {
79
+ const route = useRoute()
80
+
81
+ return (
82
+ <>
83
+ <Header />
84
+ <RouteDisplay route={route} />
85
+ <Footer />
86
+ </>
87
+ )
88
+ })
89
+ ```
90
+
91
+ Only reach for this when the shell needs control over how the active route is displayed. For normal site layouts, keep `_app.tsx` boring.
92
+
93
+ ## App Data Versus View Data [#app-data-versus-view-data]
94
+
95
+ * Use `_app.graphql` for global data shared by many routes.
96
+ * Use `views/name.graphql` for data needed by one view.
97
+ * Use `queries/*.graphql` and generated hooks for interactive data that changes after the route has rendered.
@@ -0,0 +1,82 @@
1
+ # Page Templates (/docs/views/page-templates)
2
+
3
+ **Selectable templates for pages and post types**
4
+
5
+ Most views are chosen automatically by the WordPress template hierarchy. A page template is different: it is a selectable template shown in the WordPress editor.
6
+
7
+ Use page templates for pages with a specific layout or purpose, such as a homepage, landing page, program page, or custom index page.
8
+
9
+ ## Define A Template [#define-a-template]
10
+
11
+ Create a view and export `meta: ViewMeta` with `templateName`.
12
+
13
+ ```tsx filename="views/template-home.tsx"
14
+ import { ContentBlocks } from "eddev/blocks"
15
+ import { defineView } from "eddev/views"
16
+
17
+ export const meta: ViewMeta = {
18
+ templateName: "Homepage",
19
+ }
20
+
21
+ export default defineView("template-home", (props) => {
22
+ return <ContentBlocks blocks={props.page?.contentBlocks} />
23
+ })
24
+ ```
25
+
26
+ Then pair it with a normal view query:
27
+
28
+ ```graphql filename="views/template-home.graphql"
29
+ query Homepage($postId: ID!) {
30
+ page(id: $postId, idType: DATABASE_ID) {
31
+ contentBlocks
32
+ }
33
+ }
34
+ ```
35
+
36
+ The filename still matters. `views/template-home.tsx` should call `defineView("template-home", ...)`.
37
+
38
+ ## Naming [#naming]
39
+
40
+ Prefer `template-*` for selectable page templates. It keeps them visually separate from WordPress hierarchy views like `page`, `single`, and `archive-case-study`.
41
+
42
+ `front-page.tsx` is still useful when WordPress should choose the site front page automatically. Use `template-home.tsx` when authors need to pick a homepage-style template from the editor.
43
+
44
+ ## Post Types [#post-types]
45
+
46
+ By default, a `templateName` view is available for pages. Set `postType` when the template should be selectable on another post type.
47
+
48
+ ```tsx filename="views/template-editorial.tsx"
49
+ import { defineView } from "eddev/views"
50
+
51
+ export const meta: ViewMeta = {
52
+ templateName: "Editorial",
53
+ postType: "article",
54
+ }
55
+
56
+ export default defineView("template-editorial", (props) => {
57
+ return <main>{props.article?.title}</main>
58
+ })
59
+ ```
60
+
61
+ Use an array when the same template applies to several post types:
62
+
63
+ ```ts
64
+ export const meta: ViewMeta = {
65
+ templateName: "Landing Page",
66
+ postType: ["page", "campaign"],
67
+ }
68
+ ```
69
+
70
+ ## View Metadata [#view-metadata]
71
+
72
+ The public `ViewMeta` fields are intentionally small:
73
+
74
+ * `templateName` - the label shown in the WordPress template dropdown
75
+ * `postType` - the post type or post types where the template is available
76
+ * `cache` - whether the view query can be cached
77
+
78
+ Most templates only need `templateName`.
79
+
80
+ ## Custom Routes [#custom-routes]
81
+
82
+ Page templates are selected on WordPress posts. If the URL does not represent a WordPress post, use a [custom route](/docs/routing/custom) that points to a view instead.