From 95aad1d0a339ae0831dcf8437916246b3b6d23c2 Mon Sep 17 00:00:00 2001 From: Thomas Boutell Date: Tue, 12 May 2026 09:34:27 -0400 Subject: [PATCH 1/2] Reapply "fix links, add jsx guide" This reverts commit 94bf25c8465c3490bc4e522c321f4ec62b7d612f. --- docs/.vitepress/sidebarGuide.js | 1 + docs/guide/jsx-templates.md | 399 ++++++++++++++++++++++++ docs/guide/using-sqlite-and-postgres.md | 6 +- 3 files changed, 403 insertions(+), 3 deletions(-) create mode 100644 docs/guide/jsx-templates.md diff --git a/docs/.vitepress/sidebarGuide.js b/docs/.vitepress/sidebarGuide.js index 9fe95a30..d2b80b9f 100644 --- a/docs/.vitepress/sidebarGuide.js +++ b/docs/.vitepress/sidebarGuide.js @@ -94,6 +94,7 @@ const sidebarGuide = [ { text: 'Template Filters', link: 'guide/template-filters.md' }, { text: 'Template Fragments', link: 'guide/fragments.md' }, { text: 'Async Components', link: 'guide/async-components.md' }, + { text: 'JSX Templates', link: 'guide/jsx-templates.md' }, { text: 'Custom Nunjucks Tags', link: 'reference/template-tags.md' }, ] }, diff --git a/docs/guide/jsx-templates.md b/docs/guide/jsx-templates.md new file mode 100644 index 00000000..39b23f2b --- /dev/null +++ b/docs/guide/jsx-templates.md @@ -0,0 +1,399 @@ +# JSX templates + +Apostrophe page, widget, and component templates can be written in **JSX** as an alternative to [Nunjucks](/guide/templating.md). For developers already comfortable with React or another JSX-aware framework, this means modern editor support, real JavaScript control flow, and accurate error reporting with source maps, without standing up a separate front-end project. + +::: info +This guide assumes you have written JSX before. It focuses on the Apostrophe-specific equivalents of Nunjucks features rather than on JSX itself. +::: + +JSX templates are a server-side rendering option. They do **not** imply React: there is no virtual DOM, no client runtime, and no front-end framework requirement. JSX here is simply an alternate JavaScript syntax that accommodates inline markup, evaluated on the server in the same place Nunjucks would have run. + +JSX interoperates with Nunjucks in one direction: a `.jsx` template can extend or include a `.html` template (with block overrides where appropriate), but a `.html` template cannot extend or include a `.jsx` template. In practice this means you migrate a project from the leaves up, converting individual page and widget templates to JSX while keeping `layout.html` and the core Nunjucks templates in place. See [Migration order](#migration-order) for the rules. + +## File location and naming + +JSX templates live in the same `views/` directories as Nunjucks templates, and Apostrophe finds them with the same lookup rules: + +``` +modules/default-page/views/page.jsx +modules/two-column-widget/views/widget.jsx +modules/blog/views/newest.jsx +views/layout.jsx +``` + +When both `page.jsx` and `page.html` exist for the same module, the `.jsx` version is used. Rename a single `.html` file to `.jsx` (and convert its contents) to migrate it; no other configuration is required. + +## Anatomy of a JSX template + +A JSX template **exports a default function** that returns markup. The function takes two arguments: + +1. **`data`**: the same data object you would have referenced as `data.*` in Nunjucks. Destructure the props you need, just as you would in a React component. +2. **An object of Apostrophe template helpers**: `{ apos, helpers, Area, Component, Extend, Template, Widget }`. + + + +```jsx +export default function({ page }, { Area }) { + return ( + <> +

{page.title}

+ + + ); +} +``` + + +
+ +The function may be `async`. It does not need to be: see [Async without async](#async-without-async) below. + +### The second argument + +| Name | Purpose | +| ---- | ------- | +| `apos` | The same object the rest of Apostrophe calls `self.apos`. Call any module method directly; JSX templates can `await`. | +| `helpers` | The Nunjucks-oriented helper functions (mostly thin wrappers around `apos.util` and related modules). Use these when you want the exact behavior of an existing Nunjucks helper or filter. | +| `Area` | Renders an area. Replaces `{% area ... %}`. | +| `Component` | Invokes an [async component](/guide/async-components.md). Replaces `{% component ... %}`. | +| `Template` | Renders another template by name with **include semantics**: props are passed as data. Replaces `{% include %}`. Against a JSX target, also serves as `{% extends %}` because props *are* data. | +| `Extend` | Renders another template by name with **extends semantics**. Against a Nunjucks target, props become named `{% block %}` overrides. Against a JSX target, behaves identically to `Template`. | +| `Widget` | Renders a single widget directly. Only needed if you are reimplementing `area.html` in JSX. | + +## Nunjucks to JSX cheat sheet + +### Interpolation + +```nunjucks +

{{ data.page.title }}

+``` + +```jsx +

{page.title}

+``` + +Note the cognitive shift: in JSX, `data` is the first function argument. Destructure it once at the top and reference fields directly rather than as `data.page.title`. + +### Conditionals + +```nunjucks +{% if data.user %} + Log out +{% endif %} +``` + +```jsx +{user && Log out} +``` + +For if/else, the ternary is the usual idiom: + +```jsx +{user + ? Log out + : Log in} +``` + +### Loops + +```nunjucks +{% for product in data.products %} +
  • {{ product.title }}
  • +{% endfor %} +``` + +```jsx +{products.map(product => ( +
  • + {product.title} +
  • +))} +``` + +::: info +React-flavored attributes like `key` and `ref` are accepted but ignored. They exist in React to help the client-side reconciler match elements across renders; there is no reconciler here, so they have nothing to do. Don't bother adding them. +::: + +### Areas + +```nunjucks +{% area data.page, 'main' %} +``` + +```jsx + +``` + +[Context options](/guide/areas-and-widgets.md#passing-context-options) become an ordinary prop: + +```jsx + +``` + +### Async components + +```nunjucks +{% component 'product:newest' with { max: 3 } %} +``` + +```jsx + +``` + +The component function defined in `modules/product/index.js` is invoked exactly as before. Apostrophe locates its template (`.jsx` first, then `.html`) using the same rules as Nunjucks. + +Because JSX templates can run `async` code on their own (calling any method of any Apostrophe module directly), many components that previously existed only to expose async data to a template are no longer strictly necessary. They remain useful when you want a named, reusable separation of concerns. + +### Including another template + +```nunjucks +{% include "footer.html" %} +``` + +```jsx +