33## Why use it?
44- clean syntax
55- fully-typed
6- - speed -- faster even than jinja
6+ - speed -- often faster than jinja
77- zero dependencies
88- escaped by default
99- usually renders fewer bytes than templating
1818``` python
1919from simple_html import h1, render
2020
21+
2122node = h1(" Hello World!" )
2223
2324render(node)
@@ -36,6 +37,7 @@ Here's a fuller-featured example:
3637``` python
3738from simple_html import render, DOCTYPE_HTML5 , html, head, title, body, h1, div, p, br, ul, li
3839
40+
3941render(
4042 DOCTYPE_HTML5 ,
4143 html(
@@ -79,6 +81,7 @@ As you might have noticed, there are several ways to use `Tag`s:
7981``` python
8082from simple_html import br, div, h1, img, span, render
8183
84+
8285# raw node renders to empty tag
8386render(br)
8487# <br/>
@@ -106,6 +109,7 @@ escaped by default; `SafeString`s can be used to bypass escaping.
106109``` python
107110from simple_html import br, p, SafeString, render
108111
112+
109113node = p(" Escaped & stuff" ,
110114 br,
111115 SafeString(" Not escaped & stuff" ))
@@ -121,6 +125,7 @@ that Tag attributes with `None` as the value will only render the attribute name
121125``` python
122126from simple_html import div, render
123127
128+
124129node = div({" empty-str-attribute" : " " ,
125130 " key-only-attr" : None })
126131
@@ -133,6 +138,7 @@ String attributes are escaped by default -- both keys and values. You can use `S
133138``` python
134139from simple_html import div, render, SafeString
135140
141+
136142render(
137143 div({" <bad>" :" </also bad>" })
138144)
@@ -149,6 +155,7 @@ You can also use `int`, `float`, and `Decimal` instances for attribute values.
149155from decimal import Decimal
150156from simple_html import div, render, SafeString
151157
158+
152159render(
153160 div({" x" : 1 , " y" : 2.3 , " z" : Decimal(' 3.45' )})
154161)
@@ -161,6 +168,7 @@ You can render inline CSS styles with `render_styles`:
161168``` python
162169from simple_html import div, render, render_styles
163170
171+
164172styles = render_styles({" min-width" : " 25px" })
165173
166174node = div({" style" : styles}, " cool" )
@@ -184,6 +192,7 @@ You can pass many items as a `Tag`'s children using `*args`, lists or generators
184192from typing import Generator
185193from simple_html import div, render, Node, br, p
186194
195+
187196div(
188197 * [" neat" , br], p(" cool" )
189198)
@@ -214,6 +223,7 @@ For convenience, most common tags are provided, but you can also create your own
214223``` python
215224from simple_html import Tag, render
216225
226+
217227custom_elem = Tag(" custom-elem" )
218228
219229# works the same as any other tag
@@ -225,3 +235,89 @@ node = custom_elem(
225235render(node)
226236# <custom-elem id="some-custom-elem-id">Wow</custom-elem>
227237```
238+
239+ ### Optimization
240+
241+ #### ` prerender `
242+
243+ ` prerender ` is a very simple function. It just ` render ` s a ` Node ` and puts the resulting string inside
244+ a ` SafeString ` (so its contents won't be escaped again). It's most useful for prerendering at the module level,
245+ which ensures the render operation happens only once. A simple use case might be a website's footer:
246+
247+ ``` python
248+ from simple_html import SafeString, prerender, footer, div, a, head, body, title, h1, html, render
249+
250+
251+ prerendered_footer: SafeString = prerender(
252+ footer(
253+ div(a({" href" : " /about" }, " About Us" )),
254+ div(a({" href" : " /blog" }, " Blog" )),
255+ div(a({" href" : " /contact" }, " Contact" ))
256+ )
257+ )
258+
259+
260+ def render_page (page_title : str ) -> str :
261+ return render(
262+ html(
263+ head(title(page_title)),
264+ body(
265+ h1(page_title),
266+ prerendered_footer # this is extremely fast to render
267+ )
268+ )
269+ )
270+ ```
271+ This greatly reduces the amount of work ` render ` needs to do on the prerendered content when outputting HTML.
272+
273+ #### Caching
274+ You may want to cache rendered content. This is easy to do; the main thing to keep in
275+ mind is you'll likely want to return a ` SafeString ` . For example, here's how you might cache with ` lru_cache ` :
276+
277+ ``` python
278+ from simple_html import prerender, SafeString, h1
279+ from functools import lru_cache
280+
281+
282+ @lru_cache
283+ def greeting (name : str ) -> SafeString:
284+ return prerender(
285+ h1(f " Hello, { name} " )
286+ )
287+ ```
288+
289+ One thing to remember is that not all variants of ` Node ` are hashable, and thus cannot be passed directly to a function
290+ where the arguments constitute the cache key -- e.g. lists and generators are not hashable, but they can be
291+ valid ` Node ` s. Another way to use ` prerender ` in combination with a caching function is to prerender arguments:
292+
293+ ``` python
294+ from simple_html import prerender, SafeString, h1, div, html, body, head, ul, li
295+ from functools import lru_cache
296+
297+
298+ @lru_cache
299+ def cached_content (children : SafeString) -> SafeString:
300+ return prerender(
301+ div(
302+ h1(" This content is cached according to the content of the children" ),
303+ children,
304+ # presumably this function would have a lot more elements for it to be worth
305+ # the caching overhead
306+ )
307+ )
308+
309+ def page (words_to_render : list[str ]):
310+ return html(
311+ head,
312+ body(
313+ cached_content(
314+ prerender(ul([
315+ li(word) for word in words_to_render
316+ ]))
317+ )
318+ )
319+ )
320+ ```
321+ Keep in mind that using ` prerender ` on dynamic content -- not at the module level -- still incurs all the overhead
322+ of ` render ` each time that content is rendered, so, for this approach to make sense, the prerendered content should
323+ be a small portion of the full content of the ` cached_content ` function.
0 commit comments