NewYour coding agent can read the release notes before it upgrades.Set up the MCP server →
PyPI · #4740 most downloaded on PyPI
Tools for HTML generation and output.
Last release 4 months ago
21 May 2026
Ships unpredictably
gaps range from 9 days to 1.5 years
Nearly every release is documented
notes for 15 of 16 stable releases
Nothing withdrawn
no release was ever pulled
4 years old
16 releases · first in 2022
One column per quarter.
Tagifiable.tagify()'s return annotation is now Tagified, a new type alias for the union of fully-tagified shapes (TagifiedNode | float | None | Sequen
Tagifiable.tagify()'s return annotation is now Tagified, a new type alias for the union of fully-tagified shapes (TagifiedNode | float | None | Sequence[Tagified], mirroring TagChild). Custom .tagify() implementations annotated with bare TagList / Tag return types should switch to -> Tagified, or drop the annotation. Runtime behavior of correct implementations is unchanged. (#105, #116)
The result of .tagify() is now immutable. Calling .append, .extend, .insert, .add_class, __setitem__, or the context-manager with form on a tagified value raises AttributeError and is a static type error. Mutate on the buildable Tag / TagList side, then call .tagify() once to produce the render-ready result. (#116)
TagList.tagify() now raises TypeError at the boundary when a child's .tagify() returns un-tagified content (e.g. a bare TagList containing a still-Tagifiable object). The error names the offending class and slot index so buggy .tagify() implementations surface at the source. The render-time RuntimeError for the same family of violations has been clarified. (#7, #105, #112, #116)
Exported the new tagified sibling classes TagifiedTag and TagifiedTagList. Use them in narrow annotations (def f(t: TagifiedTag): ...) and isinstance checks on .tagify() output. (#116)
Added is_tagified(x) for runtime distinguishability between buildable Tag / TagList and their tagified counterparts. Returns a TypeIs[...] so pyright narrows at call sites. (#116)
typing-extensions floor to >=4.12.0.Switched the contributor workflow to uv and reorganized Makefile targets. Run make setup (or make ai-setup) to bootstrap a dev environment, and make help to list available targets. pip install -e ".[dev,test]" no longer works since dev and test are now PEP 735 dependency groups; use uv sync --all-groups instead. (#107)
Replaced black, isort, and flake8 with ruff for linting and formatting. Run make format to auto-fix, or make check-format to verify. (#110)
HTMLDocument.save_html() now explicitly uses encoding="utf-8" when writing files, fixing UnicodeEncodeError on Windows when HTML contains non-ASCII ch
HTMLDocument.save_html() now explicitly uses encoding="utf-8" when writing files, fixing UnicodeEncodeError on Windows when HTML contains non-ASCII characters (e.g., Unicode minus sign U+2212 from matplotlib SVG output). (#102)HTML no longer inherits from str. It now inherits from collections.UserString. This was done to avoid confusion between str and HTML objects.
HTML no longer inherits from str. It now inherits from collections.UserString. This was done to avoid confusion between str and HTML objects. (#86)
TagList no longer inherits from list. It now inherits from collections.UserList. This was done to avoid confusion between list and TagList objects. (#97)
Tag and TagList's method .get_html_string() now both return str instead of HTML. (#86)
Strings added to HTML objects, now return HTML objects. E.g. HTML_value + str_value and str_value + HTML_value both return HTML objects. To maintain a str result, call str() on your HTML objects before adding them to other strings values. (#86)
Items added to TagList objects, now return TagList objects. E.g. TagList_value + arr_value and arr_value + TagList_value both return new TagList objects. To maintain a list result, call list() on your TagList objects before combining them to other list objects. (#97)
Exported ReprHtml protocol class. If an object has a _repr_html_ method, then it is of instance ReprHtml. (#86)
Exported is_tag_node() and is_tag_child() functions that utilize typing.TypeIs to narrow TagNode and TagChild type variables, respectively. (#86)
Exported consolidate_attrs(*args, **kwargs) function. This function will combine the TagAttrs (supplied in *args) with TagAttrValues (supplied in **kwargs) into a single TagAttrs object. In addition, it will also return all *args that are not dictionary as a list of unaltered TagChild objects. (#86)
The Tag method .add_style(style=) added support for HTML objects in addition to str values. (#86)
HTMLTextDocument() returning extracted HTMLDependency()s in a non-determistic order. (#95)HTML tags in docstrings are now escaped.
The HTMLDependency.copy() method can now correctly copy folders in depenendencies that both include directories and have all_files=True.
HTMLDependency.copy() method can now correctly copy folders in depenendencies that both include directories and have all_files=True. (#87)Tag objects can now be used as context managers, as in with tags.div():. When used this way, then inside the with block, sys.displayhook is replaced w
Tag objects can now be used as context managers, as in with tags.div():. When used this way, then inside the with block, sys.displayhook is replaced with a function which adds items as children to the Tag. This is meant to be used with Shiny Express, Quarto, or Jupyter. (#76)
Added a function wrap_displayhook_handler(). This alliows displayhooks to delegate their logic for handling various kinds of objects (like Tag objects and objects with a _repr_html()) to this function. (#77)
Objects with a _repr_html_ method can now appear as children of Tag/TagList objects.
Objects with a _repr_html_ method can now appear as children of Tag/TagList objects. (#74)
Changed the type annotation of _add_ws from bool to TagAttrValue. This makes it easier to write functions which call Tag functions and pass along **kwargs. (#67)
Changed the type annotation of collapse_ from str to str | float | None. This makes it easier to write calls to css() pass along **kwargs. (#68)
Enhanced the type definition of TagAttrs to include TagAttrDict, the type of a Tag's attrs property. (#55)
For HTMLTextDocument objects, deduplicate HTML dependencies. (#72)
Switched from setup.cfg and setup.py to pyproject.toml. (#73)
Fixed deserialization of JSON HTML dependencies when they contained newline characters.
Added HTMLTextDocument class, which takes as input a string representation of an HTML document.
Added HTMLTextDocument class, which takes as input a string representation of an HTML document. (#61)
Added htmltools.html_dependency_render_mode. If this is set to "json", then HTMLDependency objects will be rendered as JSON inside of <script> tags. (#61)
Added Tag methods remove_class and add_style.
Added Tag methods remove_class and add_style. (#57)
Added support for Tag's add_class(prepend=). (#57)
Fixed the stype signature of the TagFunction protocol class.
TagFunction protocol class.Added support for URL based HTMLDependency objects.
Added support for URL based HTMLDependency objects. (#53)
Tag functions now have a boolean parameter _add_ws, which determines if the tag should be surrounded by whitespace. Tags which are normally block elements (like div) have this default to True, and tags which are normally inline elements (like span) have this default to False. This makes it possible to create HTML where neighboring elements have no whitespace between them. For example, span(span("a"), span("b")) will now yield <span><span>a</span><span>b</span></span>. (#54)
Changed types hints for Tag functions. The new types are TagChild, TagNode, TagAttrValue, and TagAttrs.
Changed types hints for Tag functions. The new types are TagChild, TagNode, TagAttrValue, and TagAttrs. (#51)
Add public-facing html_escape function.
Added alias for htmltools._util._package_dir function, which was used by shinywidgets 0.1.4.
htmltools._util._package_dir function, which was used by shinywidgets 0.1.4.Fixed path handling on Windows.
Nothing published for this version
Your coding agent can read these notes before it upgrades. Set up the MCP server →