{"id":16125893,"url":"https://github.com/alexandergugel/nate","last_synced_at":"2026-02-04T00:04:15.618Z","repository":{"id":57444965,"uuid":"308915989","full_name":"alexanderGugel/nate","owner":"alexanderGugel","description":"📝   nate makes generating HTML fun.","archived":false,"fork":false,"pushed_at":"2020-11-19T02:29:31.000Z","size":1956,"stargazers_count":3,"open_issues_count":0,"forks_count":0,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-02-28T09:56:33.736Z","etag":null,"topics":["html","python","template","template-engine","template-engine-html","website"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/alexanderGugel.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2020-10-31T15:47:32.000Z","updated_at":"2022-01-27T06:42:44.000Z","dependencies_parsed_at":"2022-09-26T17:30:42.152Z","dependency_job_id":null,"html_url":"https://github.com/alexanderGugel/nate","commit_stats":null,"previous_names":[],"tags_count":6,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alexanderGugel%2Fnate","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alexanderGugel%2Fnate/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alexanderGugel%2Fnate/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alexanderGugel%2Fnate/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/alexanderGugel","download_url":"https://codeload.github.com/alexanderGugel/nate/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243928249,"owners_count":20370258,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["html","python","template","template-engine","template-engine-html","website"],"created_at":"2024-10-09T21:33:37.831Z","updated_at":"2026-02-04T00:04:15.576Z","avatar_url":"https://github.com/alexanderGugel.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"![Library](https://github.com/alexanderGugel/nate/raw/master/assets/alfons-morales-YLSwjSy7stw-unsplash.jpg)\n\n# nate\n\n[![PyPI](https://img.shields.io/pypi/v/nate)](https://pypi.org/project/nate/) [![PyPI - License](https://img.shields.io/pypi/l/nate)](https://pypi.org/project/nate/) [![Build Status](https://travis-ci.com/alexanderGugel/nate.svg?branch=master)](https://travis-ci.com/alexanderGugel/nate)\n\n**nate** makes generating HTML fun. Rather than forcing you to adopt an entirely different templating language that comes with its own set of quirks, nate is built around a simple, but powerful Python DSL that enables you to easily compose trees of elements that can be translated to well-formed HTML - no more forgotten angle brackets, unbalanced tags, or unescaped user input.\n\nThink of nate as an alternative to [Jinja](https://jinja.palletsprojects.com/en/2.11.x/) or [Django templates](https://docs.djangoproject.com/en/3.1/ref/templates/language/).\n\n## Features\n\n- **Mostly typed** - The API has been designed with type safety in mind: All\n  function boundaries have type hints to ensure correctness, ease\n  documentation, and make auto-completion in the IDE of your choice a piece of\n  cake.\n- **No dependencies** - nate does not depend on any third-party libraries.\n- **Stable API** - The API itself is stable. No breaking changes are planned.\n- **Tiny** - The core library is approximately 500 eLOC, which is tiny and\n  can be manually audited on a single afternoon.\n\n## Install\n\nWith [pip](https://pip.pypa.io/en/stable/installing/) installed, run\n\n```\n$ pip install nate\n```\n\n## Usage\n\nImport the elements you need.\n\n```python\nfrom nate import Table, Thead, Th, Tr, Tbody, Td\n```\n\nConstruct your tree.\n\n```python\npoliticians = [\n    {\"first_name\": \"Theodor\", \"last_name\": \"Heuss\", \"party\": \"FDP\"},\n    {\"first_name\": \"Heinrich\", \"last_name\": \"Lübke\", \"party\": \"CDU\"},\n    {\"first_name\": \"Gustav\", \"last_name\": \"Heinemann\", \"party\": \"SPD\"},\n    # ...\n]\n\ntable = Table(\n    [\n        Thead(Th([Tr(\"First Name\"), Tr(\"Last Name\"), Tr(\"Party\")])),\n        Tbody(map(\n            lambda politician: Tr(\n                [\n                    Td(politician[\"first_name\"]),\n                    Td(politician[\"last_name\"]),\n                    Td(politician[\"party\"]),\n                ]\n            ),\n            politicians,\n        )),\n    ]\n)\n```\n\nCall `.to_html()` on your root node to serialize your tree to a string of HTML.\n\n```python\ntable.to_html()  #=\u003e \u003ctable\u003e\u003cthead\u003e....\n```\n\nRaw text nodes are escaped by default, thus making it difficult to introduce [XSS vulnerabilities](https://en.wikipedia.org/wiki/Cross-site_scripting).\n\n```python\np = P(\"\u003cscript\u003ealert('XSS');\u003c/script\u003e\")\np.to_html()  #=\u003e \u003cp\u003e\u0026lt;script\u0026gt;alert(\u0026#x27;XSS\u0026#x27;);\u0026lt;/script\u0026gt;\u003c/p\u003e\n```\n\n### Components\n\nTemplating languages tend to come with their own abstractions for building re-usable components. There is no need for those in nate, given that component hierarchies can easily be composed using plain Python functions.\n\n```python\nfrom nate import Div, H1, P, BaseTag\n\n\ndef MyComponent(title: str, description: str) -\u003e BaseTag:\n    return Div(\n        children=[\n            H1(title),\n            P(description),\n        ],\n        class_=\"my-component\",\n    )\n\ncomponent = MyComponent(\n    title=\"My title\",\n    description=\"My description\",\n)\n\ncomponent.to_html()  #=\u003e \u003cdiv class=\"my-component\u003e...\n```\n\n### Examples\n\n\u003ctable\u003e\n\u003cthead\u003e\n\u003ctr\u003e\n\u003cth\u003e\nnate\n\u003c/th\u003e\n\u003cth\u003e\nHTML\n\u003c/th\u003e\n\u003c/tr\u003e\n\u003c/thead\u003e\n\u003ctbody\u003e\n\u003ctr\u003e\n\u003ctd\u003e\n\n```python\nTitle(\"Hello World!\").to_html()\n```\n\n\u003c/td\u003e\n\u003ctd\u003e\n\n```html\n\u003ctitle\u003eHello World!\u003c/title\u003e\n```\n\n\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\n\n```python\nsteaks = [\"Rib Eye\", \"New York Strip\", \"Porterhouse\"]\nUl(map(lambda steak: Li(steak), steaks)).to_html()\n```\n\n\u003c/td\u003e\n\u003ctd\u003e\n\n```html\n\u003cul\u003e\n  \u003cli\u003eRib Eye\u003c/li\u003e\n  \u003cli\u003eNew York Strip\u003c/li\u003e\n  \u003cli\u003ePorterhouse\u003c/li\u003e\n\u003c/ul\u003e\n```\n\n\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\n\n```python\nHtml(\n    lang=\"en\",\n    children=[\n        Head(\n            children=[\n                Meta(charset=\"utf-8\"),\n                Title(children=\"Welcome to nate!\"),\n            ]\n        ),\n        Body(\n            children=[\n                H1(\"Mission\"),\n                P(\n                    \"nate is not a template engine.\",\n                    class_=\"red\",\n                ),\n            ],\n        ),\n    ],\n).to_html()\n```\n\n\u003c/td\u003e\n\u003ctd\u003e\n\n\n```html\n\u003c!DOCTYPE html\u003e\n\u003chtml lang=\"en\"\u003e\n    \u003chead\u003e\n        \u003cmeta charset=\"utf-8\"/\u003e\n        \u003ctitle\u003eWelcome to nate!\u003c/title\u003e\n    \u003c/head\u003e\n    \u003cbody\u003e\n        \u003ch1\u003eMission\u003c/h1\u003e\n        \u003cp class=\"red\"\u003enate is not a template engine.\u003c/p\u003e\n    \u003c/body\u003e\n\u003c/html\u003e\n```\n\n\u003c/td\u003e\n\u003c/tr\u003e\n\u003c/tbody\u003e\n\u003c/table\u003e\n\n## How to contribute\n\n- Keep it simple, don't do anything too crazy. Even folks that don't know Python should be able to understand the code without any issues.\n- Design APIs with type-safety in mind.\n- If your code slows things down, it won't get merged.\n- Avoid introducing new dependencies.\n- If you don't want to follow those rules, forking is _encouraged_!\n- Ensure new code is covered by corresponding tests.\n\n## Prior works of art\n\n- [lamernews](https://github.com/antirez/lamernews) - While written in Ruby, the [`page.rb`](https://github.com/antirez/lamernews/blob/master/page.rb) library inspired this project.\n- [hyperscript](https://github.com/hyperhype/hyperscript) - Pure JavaScript alternative to JSX.\n- [hyperpython](https://github.com/ejplatform/hyperpython) - Python interpretation of hyperscript.\n- [XHPy](https://pypi.org/project/xhpy/) - Extends Python syntax such that XML document fragments become valid Python expressions. Based off XHP, a similar framework for PHP.\n\n## License\n\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Falexandergugel%2Fnate","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Falexandergugel%2Fnate","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Falexandergugel%2Fnate/lists"}