Duck Framework 2.4.0 is here, released October 27, 2026. This is our biggest update yet for the Python web framework: the first step toward Duck Native, faster and smarter HTTP caching, a richer component library, and a long list of reliability fixes.
Upgrade now and see what’s new.
The Big Wins
- Duck Native (first stage): manage native apps with the new
duck nativecommand. - Smarter HTTP caching:
ETag,Last-Modified,304 Not Modified, andIf-Rangesupport. - Click effects out of the box: new
RippleExtensionandHighlightExtension. - New components:
LinkButtonandIconButton. - Production-aware minification for JS, CSS, and HTML.
Duck Spreads Its Wings: Native Apps Are Coming
Web apps are only the start. Duck 2.4.0 takes the first stage toward Duck Native and ships the duck native command for managing native apps from the same toolchain you already use. Expect much more here in upcoming releases.
Cache Smarter, Serve Faster
Static files and downloads now play nicely with browser and CDN caches.
FileIOStream.etagandFileIOStream.last_modifiedcome from a singleos.stat()call (st_sizeandst_mtime_ns), so cache validators are generated without reading file content.If-None-MatchandIf-Modified-Sinceare handled automatically. CacheableGET/HEADresponses are downgraded to304 Not Modifiedwhen appropriate.If-Rangeis now supported for byte-range requests. A range is only honored if it matches the current strongETag(orLast-Modifiedas a fallback). If the file changed, Duck serves the full resource, which prevents corrupted partial downloads.
The result: less bandwidth, faster page loads, and more reliable resumable downloads.
Clicks That Feel Alive: Ripple and Highlight
Clickable components get a new extension system under duck.html.components.extensions.clickable:
RippleExtension: a material-style ripple on click.HighlightExtension: now applied to all clickable components by default.
Don’t want the effect? Opt out per component:
from duck.html.components.extensions.clickable import HighlightExtension
Button(exclude_extensions=[HighlightExtension])
You can also register an extension globally for a component class with register_default_extension from duck.html.components.extensions.
A Fresh Flock of Components
LinkButton(duck.html.components.link) andIconButton(duck.html.components.icon) are new.Codeis nowCodeBlock, with newfilenameandcollapsiblearguments.Selectaccepts alistortupleforoptions, in addition to a mapping.- Newlines in
text: HTML components’textproperty now supports\n, rendered as a line break. Footerhas improved mobile positioning and cleaner UI logic.Pagegainsset_webmanifestfor easier PWA setup.
Lively Stays Fresh, Synced, and Secure
- New
ensure_browser_state_syncedon the Lively WebSocket lets you easily and securely sync cookies and other state with the browser. - Page components now refresh automatically when they expire or are no longer registered, so users always get fresh content.
add_to_registryis no longer strict aboutrequestfor ownership assignment. Missing values are flagged lazily on Lively event dispatch.
Lean in Production, Friendly in Debug
JS, CSS, and HTML minification is now enabled by default only in production mode, so debugging stays pleasant.
| Setting | Purpose |
|---|---|
COMPONENTS_MINIFY_JS | Control JS minification |
COMPONENTS_MINIFY_CSS | Control CSS minification |
COMPONENTS_MINIFY_HTML | Control HTML minification |
COMPONENTS_FORCE_MINIFY_* | Force minification in debug mode |
HTML minification comes from the new duck.html.html_minify module and applies to components that accept inner HTML.
Little Upgrades, Big Smiles
duck.tomlnow supports packages with versions.- New
read_chunked(),write_chunked(),async_read_chunked(), andasync_write_chunked()helpers. - New
Spacemailprovider induck.utils.emailfor sending email through Spacemail. collect_emailhas a newfailsafeargument that logs exceptions instead of raising them.
Squashed Bugs
- Fixed infinite
session expiredloops during component and page auto-recovery. - Fixed scrolling to elements when Lively navigates to URLs with hashes.
FileIOStream.read()andAsyncFileIOStream.read()now honor the requestedsize. Previously they returned at most one chunk, and the short result was cached under the full-size key.write()now writes inchunk_sizepieces until all data is written, and the cache andon_writehooks reflect the bytes actually written.- A write that fails midway returns the partial byte count. A failure on the first chunk still raises
OSError. - Negative
sizevalues now read the whole file, matching Python’s file API. - Fixed
MCPView’srun()bug. - Fixed a
BasicExtensionlinebreak replacement bug in theinner_htmlsetter. - Fixed
StreamingRangeHttpResponseparsing theRangeheader incorrectly, which caused slightly wrong data.
Dive In: Upgrade Today
pip install --upgrade duck
Review your minification settings and any use of the old Code component (now CodeBlock) before deploying.
Where the Pond Leads Next
Duck Native is just getting started. Follow the Duck Framework blog for updates, and tell us what you want to see next. Check out this repo for more info on Duck Native.
Happy building with Duck!
