htmy 0.4.2__tar.gz → 0.9.0__tar.gz

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.
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2024 Peter Volf
3
+ Copyright (c) 2025 Peter Volf
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
@@ -1,8 +1,9 @@
1
- Metadata-Version: 2.3
1
+ Metadata-Version: 2.4
2
2
  Name: htmy
3
- Version: 0.4.2
4
- Summary: Async, pure-Python rendering engine.
3
+ Version: 0.9.0
4
+ Summary: Async, pure-Python server-side rendering engine.
5
5
  License: MIT
6
+ License-File: LICENSE
6
7
  Author: Peter Volf
7
8
  Author-email: do.volfp@gmail.com
8
9
  Requires-Python: >=3.10,<4.0
@@ -12,9 +13,12 @@ Classifier: Programming Language :: Python :: 3.10
12
13
  Classifier: Programming Language :: Python :: 3.11
13
14
  Classifier: Programming Language :: Python :: 3.12
14
15
  Classifier: Programming Language :: Python :: 3.13
16
+ Classifier: Programming Language :: Python :: 3.14
17
+ Provides-Extra: lxml
15
18
  Requires-Dist: anyio (>=4.6.2.post1,<5.0.0)
16
19
  Requires-Dist: async-lru (>=2.0.4,<3.0.0)
17
- Requires-Dist: markdown (>=3.7,<4.0)
20
+ Requires-Dist: lxml (>=6.0.0) ; extra == "lxml"
21
+ Requires-Dist: markdown (>=3.8,<4.0)
18
22
  Description-Content-Type: text/markdown
19
23
 
20
24
  ![Tests](https://github.com/volfpeter/htmy/actions/workflows/tests.yml/badge.svg)
@@ -28,22 +32,44 @@ Description-Content-Type: text/markdown
28
32
 
29
33
  # `htmy`
30
34
 
31
- **Async**, **pure-Python** rendering engine.
35
+ **Async**, **pure-Python** server-side rendering engine.
36
+
37
+ Unleash your creativity with the full power and Python, without the hassle of learning a new templating language or dealing with its limitations!
32
38
 
33
39
  ## Key features
34
40
 
35
- - **Async**-first, to let you make the best use of [modern async tools](https://github.com/timofurrer/awesome-asyncio).
41
+ - **Async**-first, to let you make the best use of modern async tools.
36
42
  - **Powerful**, React-like **context support**, so you can avoid prop-drilling.
37
43
  - Sync and async **function components** with **decorator syntax**.
38
44
  - All baseline **HTML** tags built-in.
45
+ - Support for **native HTML/XML** documents with dynamic formatting and **slot rendering**, **without custom syntax**.
39
46
  - **Markdown** support with tools for customization.
40
47
  - Async, JSON based **internationalization**.
41
48
  - Built-in, easy to use `ErrorBoundary` component for graceful error handling.
42
49
  - **Unopinionated**: use the backend, CSS, and JS frameworks of your choice, the way you want to use them.
43
50
  - Everything is **easily customizable**, from the rendering engine to components, formatting and context management.
44
51
  - Automatic and customizable **property-name conversion** from snake case to kebab case.
52
+ - **Compatible** with any other templating library through wrappers.
45
53
  - **Fully-typed**.
46
54
 
55
+ ## Testimonials
56
+
57
+ "Thank you for your work on `fasthx`, as well as `htmy`! I've never had an easier time developing with another stack." ([ref](https://github.com/volfpeter/fasthx/discussions/77))
58
+
59
+ "One of the main parts of the `FastAPI` -> `fasthx` -> `htmy` integration I'm falling in love with is its explicitness, and not too much magic happening." ([ref](https://github.com/volfpeter/fasthx/issues/54))
60
+
61
+ "Thank you for your work on `htmy` and `fasthx`, both have been very pleasant to use, and the APIs are both intuitive and simple. Great work." ([ref](https://github.com/volfpeter/fasthx/issues/54))
62
+
63
+ "I love that the language-embedded HTML generation library approach is becoming more popular." ([ref](https://www.reddit.com/r/programming/comments/1h1a0dx/comment/lzd3phw))
64
+
65
+ "Neat approach and it naturally solves the partial templates problem 👍" ([ref](https://www.reddit.com/r/Python/comments/1gp3mww/comment/lwqj4fc))
66
+
67
+ "Great API design!" ([ref](https://www.reddit.com/r/Python/comments/1gp3mww/comment/lwpdyq9))
68
+
69
+ ## Support
70
+
71
+ Consider supporting the development and maintenance of the project through [sponsoring](https://buymeacoffee.com/volfpeter), or reach out for [consulting](https://www.volfp.com/contact?subject=Consulting%20-%20HTMY) so you can get the most out of the library.
72
+
47
73
  ## Installation
48
74
 
49
75
  The package is available on PyPI and can be installed with:
@@ -52,6 +78,10 @@ The package is available on PyPI and can be installed with:
52
78
  $ pip install htmy
53
79
  ```
54
80
 
81
+ The package has the following optional dependencies:
82
+
83
+ - `lxml` *(recommended)*: When installed, it is prioritized over `xml.etree.ElementTree` and provides more secure, faster, and more flexible HTML and XML processing. It is used, for example, for Markdown processing. Install with: `pip install "htmy[lxml]"`.
84
+
55
85
  ## Concepts
56
86
 
57
87
  The entire library -- from the rendering engine itself to the built-in components -- is built around a few simple protocols and a handful of simple utility classes. This means that you can easily customize, extend, or replace basically everything in the library. Yes, even the rendering engine. The remaining parts will keep working as expected.
@@ -60,9 +90,9 @@ Also, the library doesn't rely on advanced Python features such as metaclasses o
60
90
 
61
91
  ### Components
62
92
 
63
- Every class with a sync or async `htmy(context: Context) -> Component` method is an `htmy` component (technically an `HTMYComponentType`). Strings are also components, as well as lists or tuples of `HTMYComponentType` or string objects.
93
+ Every object with a sync or async `htmy(context: Context) -> Component` method is an `htmy` component (technically an `HTMYComponentType`). Strings are also components, as well as lists or tuples of `HTMYComponentType` or string objects. In many cases though, you don't even need to create components, simple functions that return components will be sufficient -- you can find out more about this in the [Components guide](https://volfpeter.github.io/htmy/components-guide/) of the documentation.
64
94
 
65
- Using this method name enables the conversion of any of your business objects (from `TypedDicts`s or `pydantic` models to ORM classes) into components without the fear of name collision with other tools.
95
+ Using the `htmy()` method name enables the conversion of any of your pre-existing business objects (from `TypedDicts`s or `pydantic` models to ORM classes) into components without the fear of name collision or compatibility issues with other tools.
66
96
 
67
97
  Async support makes it possible to load data or execute async business logic right in your components. This can reduce the amount of boilerplate you need to write in some cases, and also gives you the freedom to split the rendering and non-rendering logic in any way you see fit.
68
98
 
@@ -109,7 +139,7 @@ user_table = html.table(
109
139
  )
110
140
  ```
111
141
 
112
- `htmy` also provides a `@component` decorator that can be used on sync or async `my_component(props: MyProps, context: Context) -> Component` functions to convert them into components (preserving the `props` typing).
142
+ `htmy` also provides a powerful `@component` decorator that can be used on sync or async `my_component(props: MyProps, context: Context) -> Component` functions and methods to convert them into components (preserving the `props` typing). You can find out more about this feature in the [Function components](https://volfpeter.github.io/htmy/function-components/) guide.
113
143
 
114
144
  Here is the same example as above, but with function components:
115
145
 
@@ -161,11 +191,11 @@ user_table = html.table(
161
191
  `htmy` has a rich set of built-in utilities and components for both HTML and other use-cases:
162
192
 
163
193
  - `html` module: a complete set of [baseline HTML tags](https://developer.mozilla.org/en-US/docs/Glossary/Baseline/Compatibility).
194
+ - `Snippet` and `Slots`: utilities for creating dynamic, customizable document snippets in their native file format (HTML, XML, Markdown, etc.), with slot rendering support.
164
195
  - `md`: `MarkdownParser` utility and `MD` component for loading, parsing, converting, and rendering markdown content.
165
196
  - `i18n`: utilities for async, JSON based internationalization.
166
197
  - `BaseTag`, `TagWithProps`, `Tag`, `WildcardTag`: base classes for custom XML tags.
167
198
  - `ErrorBoundary`, `Fragment`, `SafeStr`, `WithContext`: utilities for error handling, component wrappers, context providers, and formatting.
168
- - `Snippet`: utility class for loading and customizing document snippets from the file system.
169
199
  - `etree.ETreeConverter`: utility that converts XML to a component tree with support for custom HTMY components.
170
200
 
171
201
  ### Rendering
@@ -204,7 +234,7 @@ if __name__ == "__main__":
204
234
 
205
235
  As you could see from the code examples above, every component has a `context: Context` argument, which we haven't used so far. Context is a way to share data with the entire subtree of a component without "prop drilling".
206
236
 
207
- The context (technically a `Mapping`) is entirely managed by the renderer. Context provider components (any class with a sync or async `htmy_context() -> Context` method) add new data to the context to make it available to components in their subtree, and components can simply take what they need from the context.
237
+ The context (technically a `Mapping`) is entirely managed by the renderer. Context provider components (any class with a sync or async `htmy_context() -> Context` method) add new data to the context to make it available to components in their subtree (including themselves), and components can simply take what they need from the context.
208
238
 
209
239
  There is no restriction on what can be in the context, it can be used for anything the application needs, for example making the current user, UI preferences, themes, or formatters available to components. In fact, built-in components get their `Formatter` from the context if it contains one, to make it possible to customize tag property name and value formatting.
210
240
 
@@ -278,6 +308,7 @@ These are default tag attribute formatting rules:
278
308
  - `bool` attribute values are converted to strings (`"true"` and `"false"`).
279
309
  - `XBool.true` attributes values are converted to an empty string, and `XBool.false` values are skipped (only the attribute name is rendered).
280
310
  - `date` and `datetime` attribute values are converted to ISO strings.
311
+ - Complex values such as lists, dictionaries, tuples, and sets are JSON serialized.
281
312
 
282
313
  ### Error boundary
283
314
 
@@ -295,23 +326,51 @@ If a component executes a potentially "long-running" synchronous call, it is str
295
326
 
296
327
  In all other cases, it's best to use sync components.
297
328
 
329
+ ## XSS prevention
330
+
331
+ `htmy` does XML/HTML escaping by default. This means user input is normally sanitized and rendered safely.
332
+
333
+ There are a couple of notable exceptions to this, where components by design allow XML/HTML inputs and assume they are safe:
334
+
335
+ - `Snippet`: The primary use-case is to efficiently render XML/HTML templates, filling in placeholders with dynamic content. In this case you must ensure that the input template itself is safe!
336
+ - `MD`: This component builds on `Snippet` to support markdown inputs and performs automatic markdown to HTML conversion. You must ensure the input text is safe!
337
+
338
+ ## AI assistance
339
+
340
+ The library is registered at [Context7](https://context7.com/volfpeter).
341
+
342
+ To get good AI assistance, all you need to do is register the Context7 MCP server in your coding tool and tell the agent to use it.
343
+
344
+ Because of the similarity with native HTML, JSX, and React, you can expect good results, both for vibe coding or inline completion.
345
+
346
+ ## Compatibility and performance
347
+
348
+ By design, `htmy` is compatible with any other Python templating library, for example Jinja, through wrappers. A wrapper is simply a custom `htmy` component that internally offloads rendering to another templating framework. This makes it possible to easily combine `htmy` with other libraries, to gradually adopt it, and even to enjoy the benefits of multiple frameworks.
349
+
350
+ Performance strongly depends on how you use `htmy`. The `Snippet` component for example makes it possible to reach almost Python string formatting performance, while rendering large, deep component trees is noticeably slower than Jinja for example. Wrapping another templating library for certain use-cases, or pre-rendering components and later using `Snippet` to fill in the dynamic content can be beneficial for performance.
351
+
298
352
  ## Framework integrations
299
353
 
300
354
  FastAPI:
301
355
 
302
- - [FastHX](https://github.com/volfpeter/fasthx)
356
+ - [holm](https://github.com/volfpeter/holm): Web development framework that brings the Next.js developer experience to Python, built on FastAPI, htmy, and FastHX.
357
+ - [FastHX](https://github.com/volfpeter/fasthx): Declarative server-side rendering utility for FastAPI with built-in HTMX support.
358
+
359
+ ## External examples
360
+
361
+ - [lipsum-chat](https://github.com/volfpeter/lipsum-chat): A simple chat application using `FastAPI`, `htmx`, and `fasthx`.
303
362
 
304
363
  ## Why
305
364
 
306
365
  At one end of the spectrum, there are the complete application frameworks that combine the server (Python) and client (JavaScript) applications with the entire state management and synchronization into a single Python (an in some cases an additional JavaScript) package. Some of the most popular examples are: [Reflex](https://github.com/reflex-dev/reflex), [NiceGUI](https://github.com/zauberzeug/nicegui/), [ReactPy](https://github.com/reactive-python/reactpy), and [FastUI](https://github.com/pydantic/FastUI).
307
366
 
308
- The main benefit of these frameworks is rapid application prototyping and a very convenient developer experience (at least as long as you stay within the built-in feature set of the framework). In exchange for that, they are very opinionated (from components to frontend tooling and state management), the underlying engineering is very complex, deployment and scaling can be hard or costly, and they can be hard to migrate away from. Even with these caveats, they can be a very good choice for internal tools and application prototyping.
367
+ The main benefit of these frameworks is rapid application prototyping and a very convenient developer experience, at least as long as you stay within the built-in feature set of the framework. In exchange for that, they are very opinionated (from components to frontend tooling and state management), the underlying engineering is very complex, deployment and scaling can be hard or costly, and they can be hard to migrate away from. Even with these caveats, they can be a very good choice for internal tools and application prototyping.
309
368
 
310
- The other end of spectrum -- plain rendering engines -- is dominated by the [Jinja](https://jinja.palletsprojects.com) templating engine, which is a safe choice as it has been and will be around for a long time. The main drawbacks with Jinja are the lack of good IDE support, the complete lack of static code analysis support, and the (subjectively) ugly syntax.
369
+ The other end of spectrum -- plain rendering engines -- is dominated by the [Jinja](https://jinja.palletsprojects.com) templating engine, which is a safe choice as it has been and will be around for a long time. The main drawbacks with Jinja are the lack of good IDE support, the complete lack of static code analysis support, and the (subjectively) ugly custom template syntax.
311
370
 
312
371
  Then there are tools that aim for the middleground, usually by providing most of the benefits and drawbacks of complete application frameworks while leaving state management, client-server communication, and dynamic UI updates for the user to solve, often with some level of [HTMX](https://htmx.org/) support. This group includes libraries like [FastHTML](https://github.com/answerdotai/fasthtml) and [Ludic](https://github.com/getludic/ludic).
313
372
 
314
- The primary aim of `htmy` is to be an **async**, pure-Python rendering engine, which is as **simple**, **maintainable**, and **customizable** as possible, while still providing all the building blocks for (conveniently) creating complex and maintainable applications.
373
+ The primary aim of `htmy` is to be a `Jinja` alternative that is similarly powerful and flexible, while also providing the benefits of full IDE support, static code analysis, and native Python (and HTML, XML, markdown) syntax. Additionally, `htmy` is **async-first**, so it works great with modern async Python frameworks such as [FastAPI](https://fastapi.tiangolo.com). The library was designed to be as **simple**, **maintainable**, and **customizable** as possible, while still providing all the building blocks for creating complex web applications.
315
374
 
316
375
  ## Dependencies
317
376
 
@@ -329,7 +388,13 @@ The documentation is built with `mkdocs-material` and `mkdocstrings`.
329
388
 
330
389
  ## Contributing
331
390
 
332
- All contributions are welcome, including more documentation, examples, code, and tests. Even questions.
391
+ We welcome contributions from the community to help improve the project! Whether you're an experienced developer or just starting out, there are many ways you can contribute:
392
+
393
+ - **Discuss**: Join our [Discussion Board](https://github.com/volfpeter/htmy/discussions) to ask questions, share ideas, provide feedback, and engage with the community.
394
+ - **Document**: Help improve the documentation by fixing typos, adding examples, and updating guides to make it easier for others to use the project.
395
+ - **Develop**: Prototype requested features or pick up issues from the issue tracker.
396
+ - **Share**: Share your own project by adding a link to it in the documentation, helping others discover and benefit from your work.
397
+ - **Test**: Write tests to improve coverage and enhance reliability.
333
398
 
334
399
  ## License - MIT
335
400
 
@@ -9,22 +9,44 @@
9
9
 
10
10
  # `htmy`
11
11
 
12
- **Async**, **pure-Python** rendering engine.
12
+ **Async**, **pure-Python** server-side rendering engine.
13
+
14
+ Unleash your creativity with the full power and Python, without the hassle of learning a new templating language or dealing with its limitations!
13
15
 
14
16
  ## Key features
15
17
 
16
- - **Async**-first, to let you make the best use of [modern async tools](https://github.com/timofurrer/awesome-asyncio).
18
+ - **Async**-first, to let you make the best use of modern async tools.
17
19
  - **Powerful**, React-like **context support**, so you can avoid prop-drilling.
18
20
  - Sync and async **function components** with **decorator syntax**.
19
21
  - All baseline **HTML** tags built-in.
22
+ - Support for **native HTML/XML** documents with dynamic formatting and **slot rendering**, **without custom syntax**.
20
23
  - **Markdown** support with tools for customization.
21
24
  - Async, JSON based **internationalization**.
22
25
  - Built-in, easy to use `ErrorBoundary` component for graceful error handling.
23
26
  - **Unopinionated**: use the backend, CSS, and JS frameworks of your choice, the way you want to use them.
24
27
  - Everything is **easily customizable**, from the rendering engine to components, formatting and context management.
25
28
  - Automatic and customizable **property-name conversion** from snake case to kebab case.
29
+ - **Compatible** with any other templating library through wrappers.
26
30
  - **Fully-typed**.
27
31
 
32
+ ## Testimonials
33
+
34
+ "Thank you for your work on `fasthx`, as well as `htmy`! I've never had an easier time developing with another stack." ([ref](https://github.com/volfpeter/fasthx/discussions/77))
35
+
36
+ "One of the main parts of the `FastAPI` -> `fasthx` -> `htmy` integration I'm falling in love with is its explicitness, and not too much magic happening." ([ref](https://github.com/volfpeter/fasthx/issues/54))
37
+
38
+ "Thank you for your work on `htmy` and `fasthx`, both have been very pleasant to use, and the APIs are both intuitive and simple. Great work." ([ref](https://github.com/volfpeter/fasthx/issues/54))
39
+
40
+ "I love that the language-embedded HTML generation library approach is becoming more popular." ([ref](https://www.reddit.com/r/programming/comments/1h1a0dx/comment/lzd3phw))
41
+
42
+ "Neat approach and it naturally solves the partial templates problem 👍" ([ref](https://www.reddit.com/r/Python/comments/1gp3mww/comment/lwqj4fc))
43
+
44
+ "Great API design!" ([ref](https://www.reddit.com/r/Python/comments/1gp3mww/comment/lwpdyq9))
45
+
46
+ ## Support
47
+
48
+ Consider supporting the development and maintenance of the project through [sponsoring](https://buymeacoffee.com/volfpeter), or reach out for [consulting](https://www.volfp.com/contact?subject=Consulting%20-%20HTMY) so you can get the most out of the library.
49
+
28
50
  ## Installation
29
51
 
30
52
  The package is available on PyPI and can be installed with:
@@ -33,6 +55,10 @@ The package is available on PyPI and can be installed with:
33
55
  $ pip install htmy
34
56
  ```
35
57
 
58
+ The package has the following optional dependencies:
59
+
60
+ - `lxml` *(recommended)*: When installed, it is prioritized over `xml.etree.ElementTree` and provides more secure, faster, and more flexible HTML and XML processing. It is used, for example, for Markdown processing. Install with: `pip install "htmy[lxml]"`.
61
+
36
62
  ## Concepts
37
63
 
38
64
  The entire library -- from the rendering engine itself to the built-in components -- is built around a few simple protocols and a handful of simple utility classes. This means that you can easily customize, extend, or replace basically everything in the library. Yes, even the rendering engine. The remaining parts will keep working as expected.
@@ -41,9 +67,9 @@ Also, the library doesn't rely on advanced Python features such as metaclasses o
41
67
 
42
68
  ### Components
43
69
 
44
- Every class with a sync or async `htmy(context: Context) -> Component` method is an `htmy` component (technically an `HTMYComponentType`). Strings are also components, as well as lists or tuples of `HTMYComponentType` or string objects.
70
+ Every object with a sync or async `htmy(context: Context) -> Component` method is an `htmy` component (technically an `HTMYComponentType`). Strings are also components, as well as lists or tuples of `HTMYComponentType` or string objects. In many cases though, you don't even need to create components, simple functions that return components will be sufficient -- you can find out more about this in the [Components guide](https://volfpeter.github.io/htmy/components-guide/) of the documentation.
45
71
 
46
- Using this method name enables the conversion of any of your business objects (from `TypedDicts`s or `pydantic` models to ORM classes) into components without the fear of name collision with other tools.
72
+ Using the `htmy()` method name enables the conversion of any of your pre-existing business objects (from `TypedDicts`s or `pydantic` models to ORM classes) into components without the fear of name collision or compatibility issues with other tools.
47
73
 
48
74
  Async support makes it possible to load data or execute async business logic right in your components. This can reduce the amount of boilerplate you need to write in some cases, and also gives you the freedom to split the rendering and non-rendering logic in any way you see fit.
49
75
 
@@ -90,7 +116,7 @@ user_table = html.table(
90
116
  )
91
117
  ```
92
118
 
93
- `htmy` also provides a `@component` decorator that can be used on sync or async `my_component(props: MyProps, context: Context) -> Component` functions to convert them into components (preserving the `props` typing).
119
+ `htmy` also provides a powerful `@component` decorator that can be used on sync or async `my_component(props: MyProps, context: Context) -> Component` functions and methods to convert them into components (preserving the `props` typing). You can find out more about this feature in the [Function components](https://volfpeter.github.io/htmy/function-components/) guide.
94
120
 
95
121
  Here is the same example as above, but with function components:
96
122
 
@@ -142,11 +168,11 @@ user_table = html.table(
142
168
  `htmy` has a rich set of built-in utilities and components for both HTML and other use-cases:
143
169
 
144
170
  - `html` module: a complete set of [baseline HTML tags](https://developer.mozilla.org/en-US/docs/Glossary/Baseline/Compatibility).
171
+ - `Snippet` and `Slots`: utilities for creating dynamic, customizable document snippets in their native file format (HTML, XML, Markdown, etc.), with slot rendering support.
145
172
  - `md`: `MarkdownParser` utility and `MD` component for loading, parsing, converting, and rendering markdown content.
146
173
  - `i18n`: utilities for async, JSON based internationalization.
147
174
  - `BaseTag`, `TagWithProps`, `Tag`, `WildcardTag`: base classes for custom XML tags.
148
175
  - `ErrorBoundary`, `Fragment`, `SafeStr`, `WithContext`: utilities for error handling, component wrappers, context providers, and formatting.
149
- - `Snippet`: utility class for loading and customizing document snippets from the file system.
150
176
  - `etree.ETreeConverter`: utility that converts XML to a component tree with support for custom HTMY components.
151
177
 
152
178
  ### Rendering
@@ -185,7 +211,7 @@ if __name__ == "__main__":
185
211
 
186
212
  As you could see from the code examples above, every component has a `context: Context` argument, which we haven't used so far. Context is a way to share data with the entire subtree of a component without "prop drilling".
187
213
 
188
- The context (technically a `Mapping`) is entirely managed by the renderer. Context provider components (any class with a sync or async `htmy_context() -> Context` method) add new data to the context to make it available to components in their subtree, and components can simply take what they need from the context.
214
+ The context (technically a `Mapping`) is entirely managed by the renderer. Context provider components (any class with a sync or async `htmy_context() -> Context` method) add new data to the context to make it available to components in their subtree (including themselves), and components can simply take what they need from the context.
189
215
 
190
216
  There is no restriction on what can be in the context, it can be used for anything the application needs, for example making the current user, UI preferences, themes, or formatters available to components. In fact, built-in components get their `Formatter` from the context if it contains one, to make it possible to customize tag property name and value formatting.
191
217
 
@@ -259,6 +285,7 @@ These are default tag attribute formatting rules:
259
285
  - `bool` attribute values are converted to strings (`"true"` and `"false"`).
260
286
  - `XBool.true` attributes values are converted to an empty string, and `XBool.false` values are skipped (only the attribute name is rendered).
261
287
  - `date` and `datetime` attribute values are converted to ISO strings.
288
+ - Complex values such as lists, dictionaries, tuples, and sets are JSON serialized.
262
289
 
263
290
  ### Error boundary
264
291
 
@@ -276,23 +303,51 @@ If a component executes a potentially "long-running" synchronous call, it is str
276
303
 
277
304
  In all other cases, it's best to use sync components.
278
305
 
306
+ ## XSS prevention
307
+
308
+ `htmy` does XML/HTML escaping by default. This means user input is normally sanitized and rendered safely.
309
+
310
+ There are a couple of notable exceptions to this, where components by design allow XML/HTML inputs and assume they are safe:
311
+
312
+ - `Snippet`: The primary use-case is to efficiently render XML/HTML templates, filling in placeholders with dynamic content. In this case you must ensure that the input template itself is safe!
313
+ - `MD`: This component builds on `Snippet` to support markdown inputs and performs automatic markdown to HTML conversion. You must ensure the input text is safe!
314
+
315
+ ## AI assistance
316
+
317
+ The library is registered at [Context7](https://context7.com/volfpeter).
318
+
319
+ To get good AI assistance, all you need to do is register the Context7 MCP server in your coding tool and tell the agent to use it.
320
+
321
+ Because of the similarity with native HTML, JSX, and React, you can expect good results, both for vibe coding or inline completion.
322
+
323
+ ## Compatibility and performance
324
+
325
+ By design, `htmy` is compatible with any other Python templating library, for example Jinja, through wrappers. A wrapper is simply a custom `htmy` component that internally offloads rendering to another templating framework. This makes it possible to easily combine `htmy` with other libraries, to gradually adopt it, and even to enjoy the benefits of multiple frameworks.
326
+
327
+ Performance strongly depends on how you use `htmy`. The `Snippet` component for example makes it possible to reach almost Python string formatting performance, while rendering large, deep component trees is noticeably slower than Jinja for example. Wrapping another templating library for certain use-cases, or pre-rendering components and later using `Snippet` to fill in the dynamic content can be beneficial for performance.
328
+
279
329
  ## Framework integrations
280
330
 
281
331
  FastAPI:
282
332
 
283
- - [FastHX](https://github.com/volfpeter/fasthx)
333
+ - [holm](https://github.com/volfpeter/holm): Web development framework that brings the Next.js developer experience to Python, built on FastAPI, htmy, and FastHX.
334
+ - [FastHX](https://github.com/volfpeter/fasthx): Declarative server-side rendering utility for FastAPI with built-in HTMX support.
335
+
336
+ ## External examples
337
+
338
+ - [lipsum-chat](https://github.com/volfpeter/lipsum-chat): A simple chat application using `FastAPI`, `htmx`, and `fasthx`.
284
339
 
285
340
  ## Why
286
341
 
287
342
  At one end of the spectrum, there are the complete application frameworks that combine the server (Python) and client (JavaScript) applications with the entire state management and synchronization into a single Python (an in some cases an additional JavaScript) package. Some of the most popular examples are: [Reflex](https://github.com/reflex-dev/reflex), [NiceGUI](https://github.com/zauberzeug/nicegui/), [ReactPy](https://github.com/reactive-python/reactpy), and [FastUI](https://github.com/pydantic/FastUI).
288
343
 
289
- The main benefit of these frameworks is rapid application prototyping and a very convenient developer experience (at least as long as you stay within the built-in feature set of the framework). In exchange for that, they are very opinionated (from components to frontend tooling and state management), the underlying engineering is very complex, deployment and scaling can be hard or costly, and they can be hard to migrate away from. Even with these caveats, they can be a very good choice for internal tools and application prototyping.
344
+ The main benefit of these frameworks is rapid application prototyping and a very convenient developer experience, at least as long as you stay within the built-in feature set of the framework. In exchange for that, they are very opinionated (from components to frontend tooling and state management), the underlying engineering is very complex, deployment and scaling can be hard or costly, and they can be hard to migrate away from. Even with these caveats, they can be a very good choice for internal tools and application prototyping.
290
345
 
291
- The other end of spectrum -- plain rendering engines -- is dominated by the [Jinja](https://jinja.palletsprojects.com) templating engine, which is a safe choice as it has been and will be around for a long time. The main drawbacks with Jinja are the lack of good IDE support, the complete lack of static code analysis support, and the (subjectively) ugly syntax.
346
+ The other end of spectrum -- plain rendering engines -- is dominated by the [Jinja](https://jinja.palletsprojects.com) templating engine, which is a safe choice as it has been and will be around for a long time. The main drawbacks with Jinja are the lack of good IDE support, the complete lack of static code analysis support, and the (subjectively) ugly custom template syntax.
292
347
 
293
348
  Then there are tools that aim for the middleground, usually by providing most of the benefits and drawbacks of complete application frameworks while leaving state management, client-server communication, and dynamic UI updates for the user to solve, often with some level of [HTMX](https://htmx.org/) support. This group includes libraries like [FastHTML](https://github.com/answerdotai/fasthtml) and [Ludic](https://github.com/getludic/ludic).
294
349
 
295
- The primary aim of `htmy` is to be an **async**, pure-Python rendering engine, which is as **simple**, **maintainable**, and **customizable** as possible, while still providing all the building blocks for (conveniently) creating complex and maintainable applications.
350
+ The primary aim of `htmy` is to be a `Jinja` alternative that is similarly powerful and flexible, while also providing the benefits of full IDE support, static code analysis, and native Python (and HTML, XML, markdown) syntax. Additionally, `htmy` is **async-first**, so it works great with modern async Python frameworks such as [FastAPI](https://fastapi.tiangolo.com). The library was designed to be as **simple**, **maintainable**, and **customizable** as possible, while still providing all the building blocks for creating complex web applications.
296
351
 
297
352
  ## Dependencies
298
353
 
@@ -310,7 +365,13 @@ The documentation is built with `mkdocs-material` and `mkdocstrings`.
310
365
 
311
366
  ## Contributing
312
367
 
313
- All contributions are welcome, including more documentation, examples, code, and tests. Even questions.
368
+ We welcome contributions from the community to help improve the project! Whether you're an experienced developer or just starting out, there are many ways you can contribute:
369
+
370
+ - **Discuss**: Join our [Discussion Board](https://github.com/volfpeter/htmy/discussions) to ask questions, share ideas, provide feedback, and engage with the community.
371
+ - **Document**: Help improve the documentation by fixing typos, adding examples, and updating guides to make it easier for others to use the project.
372
+ - **Develop**: Prototype requested features or pick up issues from the issue tracker.
373
+ - **Share**: Share your own project by adding a link to it in the documentation, helping others discover and benefit from your work.
374
+ - **Test**: Write tests to improve coverage and enhance reliability.
314
375
 
315
376
  ## License - MIT
316
377
 
@@ -5,7 +5,6 @@ from .core import Formatter as Formatter
5
5
  from .core import Fragment as Fragment
6
6
  from .core import SafeStr as SafeStr
7
7
  from .core import SkipProperty as SkipProperty
8
- from .core import Snippet as Snippet
9
8
  from .core import Tag as Tag
10
9
  from .core import TagConfig as TagConfig
11
10
  from .core import TagWithProps as TagWithProps
@@ -13,12 +12,13 @@ from .core import Text as Text
13
12
  from .core import WildcardTag as WildcardTag
14
13
  from .core import WithContext as WithContext
15
14
  from .core import XBool as XBool
16
- from .core import component as component
17
15
  from .core import xml_format_string as xml_format_string
16
+ from .function_component import component as component
18
17
  from .renderer import Renderer as Renderer
18
+ from .snippet import Slots as Slots
19
+ from .snippet import Snippet as Snippet
19
20
  from .typing import AsyncComponent as AsyncComponent
20
21
  from .typing import AsyncContextProvider as AsyncContextProvider
21
- from .typing import AsyncFunctionComponent as AsyncFunctionComponent
22
22
  from .typing import Component as Component
23
23
  from .typing import ComponentSequence as ComponentSequence
24
24
  from .typing import ComponentType as ComponentType
@@ -26,16 +26,21 @@ from .typing import Context as Context
26
26
  from .typing import ContextKey as ContextKey
27
27
  from .typing import ContextProvider as ContextProvider
28
28
  from .typing import ContextValue as ContextValue
29
- from .typing import FunctionComponent as FunctionComponent
30
29
  from .typing import HTMYComponentType as HTMYComponentType
31
30
  from .typing import MutableContext as MutableContext
32
31
  from .typing import Properties as Properties
33
32
  from .typing import PropertyValue as PropertyValue
33
+ from .typing import RendererType as RendererType
34
+ from .typing import StrictComponentType as StrictComponentType
34
35
  from .typing import SyncComponent as SyncComponent
35
36
  from .typing import SyncContextProvider as SyncContextProvider
36
- from .typing import SyncFunctionComponent as SyncFunctionComponent
37
- from .typing import is_component_sequence as is_component_sequence
37
+ from .utils import as_component_sequence as as_component_sequence
38
+ from .utils import as_component_type as as_component_type
39
+ from .utils import is_component_sequence as is_component_sequence
40
+ from .utils import join
38
41
  from .utils import join_components as join_components
39
42
 
43
+ join_classes = join
44
+
40
45
  HTMY = Renderer
41
46
  """Deprecated alias for `Renderer`."""