DocsBuild
.nexy files
A .nexy file is a component: Python on top, HTML in the middle, CSS at the bottom.
---
from nexy import usePathname
slug: prop[str] = "untitled"
pathname = usePathname()
---
<article class="post">
<h1>{{ slug }}</h1>
<p>You are on {{ pathname }}</p>
</article>
<style>
.post { max-width: 60ch; }
</style>Frontmatter
Everything between the two --- lines is Python. Imports are resolved when the component is
compiled; the rest runs for every request, so it can read request state.
| What | Example |
|---|---|
| Import a module | import json, from datetime import datetime |
| Import a component | from "@components/card.nexy" import Card |
| Declare a property | title: prop[str] = "Default" |
| Compute a value | year = datetime.now().year |
| Read the request | pathname = usePathname() |
| Cache the page (ISR) | revalidate = 60 |
Properties
Properties are the component's API. prop[type] declares the type, = value gives a default. A
property without a default must be passed by the parent.
Imports that start with an alias are resolved through useAliases. The common setup maps @ to
src/, which makes @components/card.nexy mean src/components/card.nexy.
Template
Below the frontmatter is HTML with Jinja2. Variables from the frontmatter and the properties are in scope.
{% for item in items %}
<li>{{ item.name }}</li>
{% endfor %}
{% if user %}
<p>Welcome, {{ user.name }}</p>
{% endif %}
Autoescaping is off: templates are the project's own HTML, so {{ value }} is inserted as
written. Escape untrusted values yourself with {{ value | e }}. | safe therefore changes nothing.
Components
Any imported callable whose name starts with an uppercase letter can be used as a tag. The compiler rewrites it to a Jinja call: attributes become keyword arguments.
---
from "@components/card.nexy" import Card
from nexy import Audio
---
<Card title="Hello">
<p>Child content</p>
</Card>
<Audio src="/theme.mp3" controls=True />The last line is exactly {{ Audio("/theme.mp3", controls=True) }}. Both syntaxes are valid for the
built-in Image, Link, Audio and Video helpers - see
components.
Children are rendered through <Slot /> (capital S) inside the child component, and through {{ children }}
inside a layout.
Pass a computed value with Jinja inside the attribute: <Icon path="{{ icon }}" size="{{ size }}" />.
A bare name such as path=icon is the text icon, not the variable. To give a component a CSS
class, declare a class_name prop: class is a Python keyword.
Styles
<style> blocks are emitted with the page that declares them. They are not scoped: a class name
used in two components is one class. Prefix or namespace your classes.
Rules that save time
- One component per file. The file name is the component name, in PascalCase (
CopyChip.nexy). A.nexycomponent is imported as a Python module: a hyphen in the name (copy-chip.nexy) does not compile. - Declare props before using them: an undeclared variable renders as an empty string, because Jinja2's undefined is silent here.
- Keep side effects inside functions - the frontmatter runs again on every request.
Next steps
- Markdown and MDX: content pages
- ISR: cache a page per path
- CSR: client-side rendering