python DOM and SSR HTML rendering library
1
stars
44
commits
Python
primary language
Sep 9, 2026
updated
A Python library for building and rendering HTML using a DOM and component model.
pip install git+https://github.com/u84u/palestine.git
Or clone and install locally:
git clone https://github.com/u84u/palestine
cd palestine
pip install -e .
Requires Python 3.9+.
from palestine import Document, Html, Body, Div, Text
doc = Document()
html = Html()
body = Body()
doc.append_child(html)
html.append_child(body)
page = Div(class_="container")
page.append_child(Text("Hello from Palestine"))
body.append_child(page)
print(doc)
Output:
<!DOCTYPE html>
<html><body><div class="container">Hello from Palestine</div></body></html>
from palestine import Div, Text, Button
card = Div(class_="card")
card.append_child(Text("Title"))
card.append_child(Button(type="submit").append_child(Text("Click")))
# Move nodes between parents automatically
other = Div()
other.append_child(card) # Detaches from previous parent
from palestine import Div, Text, RawText
# Text is escaped automatically
div = Div()
div.append_child(Text("<script>")) # Renders as <script>
# RawText is for trusted markup
div.append_child(RawText("<svg>...</svg>")) # Renders as-is
from palestine import Div, H1, P, Text
def Card(title, content):
card = Div(class_="card")
card.append_child(H1().append_child(Text(title)))
card.append_child(P().append_child(Text(content)))
return card
page = Div()
page.append_child(Card("First", "Content here"))
page.append_child(Card("Second", "More content"))
from palestine import parse_html
doc = parse_html('<div class="box"><p>Hello</p></div>')
div = doc.query_selector(".box")
div.append_child(parse_html("<span>World</span>"))
from palestine import Template
tmpl = Template("<h1>{{ title }}</h1><p>{{ content }}</p>")
html = tmpl.render(title="My Page", content="Hello world")
from palestine import FormBuilder, FormValidator, Required, Email
form = FormBuilder(action="/submit", method="post")
form.add_text("name", "Name", required=True)
form.add_email("email", "Email", required=True)
form.add_submit("Register")
validator = FormValidator({
"name": [Required()],
"email": [Required(), Email()],
})
errors = validator.validate({"name": "", "email": "invalid"})
Palestine is built around a mutable DOM tree:
Node
├── Element (tag, attributes, children)
├── Text (escaped content)
├── RawText (trusted markup)
├── Comment
└── Document (root node)
Components are functions that compose elements. The renderer serializes the tree to HTML.
Palestine's renderer is optimized around direct DOM traversal, string accumulation, and bounded selector caching.
Reproducible benchmarks, profiling scripts, and historical baseline comparisons live in the GitHub repository under benchmarks/.
From a Git checkout:
python -m benchmarks.benchmark
python -m benchmarks.benchmark_memory
python benchmarks/profile_hotpaths.py
See benchmarks/REPORT.md for detailed results and methodology.
Early-stage project. The core DOM, rendering, and template engine are functional and tested.
The parser is built on Python's html.parser and does not implement the full WHATWG HTML parsing algorithm. It handles common cases but may differ from browser parsing on malformed input.
The API may change between versions.
Most Python HTML generation falls into two camps: template languages (Jinja2, Mako) that treat HTML as strings, or minimal builder libraries (dominate, htpy) with a narrow API.
Palestine is different in that it gives you a mutable, traversable DOM tree — the same conceptual model as the browser — with querySelector, classList, event dispatch, innerHTML, closest, and clone. If you are building a server-side rendering pipeline, a static site generator, or an email renderer where you need to inspect and mutate the tree after construction (not just write it once), Palestine is designed for that.
It has no runtime dependencies and works on Python 3.9+.
Two runnable examples are in the examples/ directory:
hello_world.py — minimal HTTP serverfile_explorer.py — file browser showing table building, conditional DOM construction, and safe path handlingRun either with: python examples/file_explorer.py
Then open http://localhost:8000
MIT
44 commits
Python
100.0%
python DOM and SSR HTML rendering library
1
stars
44
commits
Python
primary language
Sep 9, 2026
updated
A Python library for building and rendering HTML using a DOM and component model.
pip install git+https://github.com/u84u/palestine.git
Or clone and install locally:
git clone https://github.com/u84u/palestine
cd palestine
pip install -e .
Requires Python 3.9+.
from palestine import Document, Html, Body, Div, Text
doc = Document()
html = Html()
body = Body()
doc.append_child(html)
html.append_child(body)
page = Div(class_="container")
page.append_child(Text("Hello from Palestine"))
body.append_child(page)
print(doc)
Output:
<!DOCTYPE html>
<html><body><div class="container">Hello from Palestine</div></body></html>
from palestine import Div, Text, Button
card = Div(class_="card")
card.append_child(Text("Title"))
card.append_child(Button(type="submit").append_child(Text("Click")))
# Move nodes between parents automatically
other = Div()
other.append_child(card) # Detaches from previous parent
from palestine import Div, Text, RawText
# Text is escaped automatically
div = Div()
div.append_child(Text("<script>")) # Renders as <script>
# RawText is for trusted markup
div.append_child(RawText("<svg>...</svg>")) # Renders as-is
from palestine import Div, H1, P, Text
def Card(title, content):
card = Div(class_="card")
card.append_child(H1().append_child(Text(title)))
card.append_child(P().append_child(Text(content)))
return card
page = Div()
page.append_child(Card("First", "Content here"))
page.append_child(Card("Second", "More content"))
from palestine import parse_html
doc = parse_html('<div class="box"><p>Hello</p></div>')
div = doc.query_selector(".box")
div.append_child(parse_html("<span>World</span>"))
from palestine import Template
tmpl = Template("<h1>{{ title }}</h1><p>{{ content }}</p>")
html = tmpl.render(title="My Page", content="Hello world")
from palestine import FormBuilder, FormValidator, Required, Email
form = FormBuilder(action="/submit", method="post")
form.add_text("name", "Name", required=True)
form.add_email("email", "Email", required=True)
form.add_submit("Register")
validator = FormValidator({
"name": [Required()],
"email": [Required(), Email()],
})
errors = validator.validate({"name": "", "email": "invalid"})
Palestine is built around a mutable DOM tree:
Node
├── Element (tag, attributes, children)
├── Text (escaped content)
├── RawText (trusted markup)
├── Comment
└── Document (root node)
Components are functions that compose elements. The renderer serializes the tree to HTML.
Palestine's renderer is optimized around direct DOM traversal, string accumulation, and bounded selector caching.
Reproducible benchmarks, profiling scripts, and historical baseline comparisons live in the GitHub repository under benchmarks/.
From a Git checkout:
python -m benchmarks.benchmark
python -m benchmarks.benchmark_memory
python benchmarks/profile_hotpaths.py
See benchmarks/REPORT.md for detailed results and methodology.
Early-stage project. The core DOM, rendering, and template engine are functional and tested.
The parser is built on Python's html.parser and does not implement the full WHATWG HTML parsing algorithm. It handles common cases but may differ from browser parsing on malformed input.
The API may change between versions.
Most Python HTML generation falls into two camps: template languages (Jinja2, Mako) that treat HTML as strings, or minimal builder libraries (dominate, htpy) with a narrow API.
Palestine is different in that it gives you a mutable, traversable DOM tree — the same conceptual model as the browser — with querySelector, classList, event dispatch, innerHTML, closest, and clone. If you are building a server-side rendering pipeline, a static site generator, or an email renderer where you need to inspect and mutate the tree after construction (not just write it once), Palestine is designed for that.
It has no runtime dependencies and works on Python 3.9+.
Two runnable examples are in the examples/ directory:
hello_world.py — minimal HTTP serverfile_explorer.py — file browser showing table building, conditional DOM construction, and safe path handlingRun either with: python examples/file_explorer.py
Then open http://localhost:8000
MIT
44 commits
Python
100.0%