You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: README.md
+6-3Lines changed: 6 additions & 3 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -17,6 +17,7 @@ Web development framework that brings the Next.js developer experience to Python
17
17
-**Standard FastAPI** everywhere, so you can leverage the entire FastAPI ecosystem.
18
18
-**JSX-like syntax** with async support for components, thanks to `htmy`.
19
19
- First class **HTMX support** with `FastHX`.
20
+
- Built-in **Jinja support** with zero configuration.
20
21
-**Async** support everywhere, from APIs and dependencies all the way to UI components.
21
22
- Support for both **JSON** and **HTML** (server side rendering) APIs.
22
23
-**No JavaScript** dependencies
@@ -72,7 +73,7 @@ Consider supporting the development and maintenance of the project through [spon
72
73
Similarly to Next.js, `holm` is built around the concept of [file-system based routing](https://volfpeter.github.io/holm/file-system-based-routing). This essentially means that your code structure is automatically mapped to a matching API:
73
74
74
75
- You do not need to manually define routes, every [application component](https://volfpeter.github.io/holm/application-components) is automatically discovered and registered in the application.
75
-
- You do not need to manually wrap pages in their layouts, it is automatically done based on your application's code structure.
76
+
- You do not need to manually wrap pages in their layouts; this is automatically done based on your application's code structure.
76
77
77
78
You can find all the necessary details on the [Application components](https://volfpeter.github.io/holm/application-components) page, and the [Quick start guide](https://volfpeter.github.io/holm/guides/quick-start-guide) can walk you through the process of creating your first application. The two are complementary documents, reading both is strongly recommended.
78
79
@@ -115,7 +116,9 @@ When building web applications, performance should be compared to an application
115
116
116
117
### Templating language support
117
118
118
-
While certain features in `holm` rely heavily on the capabilities of `htmy` (for example its context and async support), you can still use other DSLs or templating languages (for example Jinja) in your application if you would like to. All you need to do is write a simple wrapper `htmy` component that internally offloads rendering to your framework of choice. You can find out more about this in the [htmy documentation](https://volfpeter.github.io/htmy/#compatibility-and-performance).
119
+
`holm` ships with built-in **Jinja** support: `layout.jinja` files work out of the box as layouts, and `holm.JinjaTemplate` components render with zero configuration when `holm` owns the `htmy` renderer. See the [Jinja layout guide](https://volfpeter.github.io/holm/guides/jinja-layout) for details.
120
+
121
+
Since `htmy` is the rendering engine, you can also use other DSLs or templating languages by writing a simple wrapper `htmy` component that offloads rendering to your framework of choice. You can find out more about this in the [htmy documentation](https://volfpeter.github.io/htmy/#compatibility-and-performance).
119
122
120
123
## Development
121
124
@@ -153,7 +156,7 @@ The most prominent frameworks in this category are Reflex and NiceGUI. They both
153
156
154
157
This category includes frameworks like FastHTML or Ludic, and this is where `holm` belongs as well, but it has some key differentiators.
155
158
156
-
First, `holm` brings the Next.js developer experience to Python with file-system based routing, automatic layout composition, and dynamic page metadata creation, and more. Thanks to `htmy`, it supports async code throughout the stack, even in components, and it also solves the prop drilling problem. While being built with `htmy`, it is easy to integrate with other templating libraries, like`Jinja` or`htpy`. And it provides all these features using standard, simple FastAPI patterns.
159
+
First, `holm` brings the Next.js developer experience to Python with file-system based routing, automatic layout composition, and dynamic page metadata creation, and more. Thanks to `htmy`, it supports async code throughout the stack, even in components, and it also solves the prop drilling problem. While being built with `htmy`, it ships with built-in **`Jinja`** support and remains easy to integrate with other templating libraries, like `htpy`. And it provides all these features using standard, simple FastAPI patterns.
Copy file name to clipboardExpand all lines: docs/application-components.md
+10-9Lines changed: 10 additions & 9 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -6,9 +6,9 @@ This section summarizes the different components of `holm` applications. If you
6
6
7
7
*Layouts* are defined in the `layout.py` modules of packages as a callable `layout` variable (note that for example classes with an `__init__()` method, or `htmy.Component`s are callable, so they also qualify).
8
8
9
-
As an alternative to Python-based layouts, you can also create `layout.html` files in packages, from which a Python layout equivalent is automatically generated.
9
+
As an alternative to Python-based layouts, you can also create `layout.jinja` files in packages. These are standard Jinja2 templates, with the wrapped page (or any other child components) passed in as pre-rendered named slots. See the [Jinja layout guide](guides/jinja-layout.md) for a complete walkthrough.
10
10
11
-
Note: If both `layout.py` and `layout.html` exist in the same package, the Python layout takes precedence. HTML layouts at the application root level require a Python package structure.
11
+
Note: If both `layout.py` and `layout.jinja` exist in the same package, the Python layout takes precedence.
12
12
13
13
Rules for *Python layouts*:
14
14
@@ -17,14 +17,15 @@ Rules for *Python layouts*:
17
17
-`layout` can have additional arguments (position or keyword and keyword-only, but not positional-only). These arguments must be FastAPI dependencies. They will be automatically resolved during each request.
18
18
- Returning a tuple or a list from a layout is **not allowed** unless the value is a `htmy.ComponentSequence`. Tuples and lists are always interpreted and treated as component sequences, so you don't need to track what kinds of components pages and layouts return. See `htmy.is_component_sequence()` for more information.
19
19
20
-
Rules for *HTML layouts*:
20
+
Rules for *Jinja layouts*:
21
21
22
-
- Must follow Python `str.format()` syntax.
23
-
-[Page metadata](#page-metadata) is accessible as `metadata`.
24
-
- The current FastAPI request is accessible as `request`.
25
-
- Use the `<!-- slot[name] -->` slot syntax for content injection (processed by `htmy.Snippet` and `htmy.Slots` components).
26
-
- The default slot is `children` (`<!-- slot[children] -->`), which is filled with the return value of the wrapped layout or page.
27
-
- The entire HTML layout processing pipeline can be customized using the `str_to_layout` argument of `App()`, allowing you to plug in tools like Jinja2 for example.
22
+
- Must be valid Jinja2 templates.
23
+
-[Page metadata](#page-metadata) is accessible as `metadata`, e.g. `{{ metadata.title }}`.
24
+
- The current FastAPI request is accessible as `request`, e.g. `{{ request.url.path }}`.
25
+
- Route parameters (resolved FastAPI dependencies) are accessible as `route_params`, e.g. `{{ route_params.current_user }}`.
26
+
- Use the `{{ slots.name }}` syntax for content injection. The default slot is `children` (`{{ slots.children }}`), which is filled with the return value of the wrapped layout or page.
27
+
- Return a mapping from a page or layout to provide content for multiple named slots.
28
+
- App-wide default slots can be configured with the `layout_slots` argument of `App()`, e.g. `App(layout_slots={"navbar": navbar})`.
28
29
29
30
By default, layouts automatically wrap all layouts and pages in subpackages. You can opt out of this behavior by wrapping the return value of a layout or page with the [`without_layout` utility](utilities.md#without_layout).
Copy file name to clipboardExpand all lines: docs/file-system-based-routing.md
+2-2Lines changed: 2 additions & 2 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -12,7 +12,7 @@ Going through the [Application components](application-components.md) documentat
12
12
- Underscores (`_`) are replaced with hyphens (`-`) in paths by default, unless a path segment corresponds to a path parameter, in which case the underscore is preserved.
13
13
- Special files mark application components: `holm` looks for specific filenames within your application directory to discover application components and compose your application.
14
14
-`page.py`: Creates a publicly accessible URL for a route segment.
15
-
-`layout.py`: Defines a shared UI that wraps a route segment and its children. `layout.html` is also supported as an HTML-based alternative (requires package structure at root level), see [Application components](application-components.md).
15
+
-`layout.py`: Defines a shared UI that wraps a route segment and its children. `layout.jinja` is also supported as a Jinja2-based alternative, see [Application components](application-components.md).
16
16
-`actions.py`: Defines custom action endpoints for a route segment.
17
17
-`api.py`: Creates custom API endpoints for a route segment.
18
18
@@ -93,7 +93,7 @@ Besides the special files (`page.py`, `layout.py`, `api.py`, etc.), you can plac
93
93
94
94
In the example, `my_app/navbar.py` is not a special file, so it is not routable. It can define any components or utilities, like a `Navbar`, which can then be imported and used in the layouts, pages, or APIs of the application.
95
95
96
-
`holm` also doesn't consider anything outside the application package as an application component. We could create a `my_components/` directory next to `my_app/` and have anything in it, including `page.py` files for example. The content of this package would be ignored by `holm`, it is not within the application package.
96
+
`holm` also doesn't consider anything outside the application package as an application component. We could create a `my_components/` directory next to `my_app/` and have anything in it, including `page.py` files for example. The content of this package would be ignored by `holm`, as it is not within the application package.
Copy file name to clipboardExpand all lines: docs/guides/jinja-layout-default-slots.md
+55-59Lines changed: 55 additions & 59 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -1,38 +1,55 @@
1
-
# HTML layout with default slots
1
+
# Jinja layout default slots
2
2
3
-
This guide builds on the [HTML layout guide](html-layout.md) and the [HTML multi-slot layout guide](html-multi-slot-layout.md) to demonstrate how to use **default slots** for shared components that appear on every page.
3
+
This guide builds on the [Jinja layout guide](jinja-layout.md) and the [Jinja multi-slot layout guide](jinja-multi-slot-layout.md) to demonstrate how to use **default slots** for shared components that appear on every page.
4
4
5
5
We will cover:
6
6
7
-
- How to create a custom `str_to_layout` converter that provides default slots.
8
-
- How to build a reusable navigation component that highlights the current page.
7
+
- How to configure default slots with `App(layout_slots=...)`.
8
+
- How to render default slots in a Jinja layout.
9
9
10
-
The entire source code of this application can be found in the [examples/html-layout-default-slots](https://github.com/volfpeter/holm/tree/main/examples/html-layout-default-slots) directory of the repository.
10
+
The entire source code of this application can be found in the [examples/jinja-layout-default-slots](https://github.com/volfpeter/holm/tree/main/examples/jinja-layout-default-slots) directory of the repository.
11
11
12
12
Before you continue, make sure you have installed `holm` and either `uvicorn` or `fastapi-cli`!
13
13
14
14
## File structure
15
15
16
-
The application uses a Python package structure (required for HTML layouts):
16
+
The application uses a Python package structure:
17
17
18
18
```
19
-
html-layout-default-slots/ # Root directory
20
-
└── my_app/ # Application package
21
-
├── __init__.py # Makes this a Python package (required for HTML layouts)
22
-
├── main.py # Application entry point with custom str_to_layout
23
-
├── layout.html# Root HTML layout with navbar and children slots
24
-
├── navbar.py # Shared navigation component
25
-
├── page.py # Home page
19
+
jinja-layout-default-slots/ # Root directory
20
+
└── my_app/ # Application package
21
+
├── __init__.py
22
+
├── layout.jinja# Root Jinja layout with navbar and children slots
23
+
├── main.py # Application entry point
24
+
├── navbar.py # Default navbar component
25
+
├── page.py # Home page
26
26
└── about/
27
27
├── __init__.py
28
-
└── page.py # About page
28
+
└── page.py # About page
29
29
```
30
30
31
-
## Create the navigation component
31
+
## Create the application with a default slot
32
+
33
+
Default slots are configured when you create the `holm` application:
34
+
35
+
```python hl_lines="8"
36
+
from holm import App
37
+
38
+
from .navbar import navbar
39
+
40
+
# `layout_slots` makes the `navbar` slot available to every component
41
+
# in the application, including Jinja layouts, through
42
+
# `htmy.jinja.DefaultSlots.from_context()`.
43
+
app = App(layout_slots={"navbar": navbar})
44
+
```
45
+
46
+
`layout_slots` takes a mapping from slot name to `htmy` component. These slots are injected into the `htmy` rendering context using `htmy.jinja.DefaultSlots` and merged with any explicit slots returned by the wrapped page or layout. When there is a slot name conflict, the explicit slot provided by the wrapped page or layout takes precedence.
47
+
48
+
## Create the default navbar component
32
49
33
50
First we create `my_app/navbar.py` with a navigation component that highlights the current page:
34
51
35
-
```python hl_lines="1 10 28-29 33-38"
52
+
```python hl_lines="1 18 28"
36
53
from fasthx.htmy import CurrentRequest
37
54
from htmy import ComponentType, Context, component, html
The important bit here is the `str_to_layout_with_navbar()` function (which wraps `snippet_to_layout`), and how we pass it to `App()`.
106
-
107
-
The `default_slot_mapping` argument specifies components that will be automatically provided for slots unless the page explicitly overrides them. In this case it means HTML layouts will always have access to our `navbar` without pages having to return it themselves.
108
-
109
-
## Create the HTML layout with slots
110
-
111
-
Create `my_app/layout.html` with slots for both the navbar and page content:
0 commit comments