v1.28.0
1.28.0 (2026-07-02)
Page Versioning
Every page now keeps a full version history: a docked timeline of every publish and manual save.
page-versioning-walkthrough-tight.mp4
- Snapshots: every publish and manual save is kept as a version, with timestamps and labels.
- Preview: view any past version live on the canvas; your working draft is never touched.
- Restore: load any version back as your draft (the live page stays untouched until you publish).
- Copy across versions: copy a block/section from an older version into your current page.
- Component pinning: each version remembers the exact component versions it used, so old versions render the way they were saved. When a pinned component has a newer version, Builder flags it and lets you update.
Component Scripts
Component logic is no longer scattered across individual blocks. Components are now the single place to break logic out of Page Scripts. Each component gets one data script (for dynamic data) and one client script (for reactivity), plus props that drive how dynamic values resolve.
- Assign dynamic data via the
componentdict (likedatafor page scripts), and pass it to the client script throughcomponent.component_data. - Access the component root element with
thisin the client script, and readpropsin both scripts. - Style scoping keeps a component's styles contained to that component.
| Profile Card | Props in Components |
|---|---|
![]() |
![]() |
Click-Through Rate (CTR) Analytics
Turn on Track Clicks for any link or button and Builder captures its clicks on published pages, then reports click-through rate right in the page's analytics. It is built on the existing page-view to DuckDB to dashboard pipeline, so clicks ride the same view-tracking toggle.
- Opt-in per block via a Track Clicks toggle in the block's Link section, so only the blocks you choose are tracked (no autocapture noise).
- Top Clicks and Top Referrers sit side by side in per-page analytics.
- Click a Top Clicks row to jump straight to that block on the canvas.
Dynamic Custom HTML Attributes
Custom HTML attributes can now take dynamic values, while attribute names and static fallbacks stay editable as plain strings, reusing the existing attribute dynamic value type and the familiar zap-icon dropdown.
Screen.Recording.2026-06-23.at.10.58.10.PM.mp4
Redirect Enhancements
Redirects now come with inline documentation and syntax highlighting, plus options to set the redirect status code and forward query params.
| Before | After |
|---|---|
![]() |
![]() |
Custom 404 Page
You can now set a custom page to serve as the site's 404 / not-found page.
Performance
- Leaner block storage (~39% smaller). Empty defaults (
rawStyles:{},attributes:{},classes:[],dataKey:null, and the like) are now stripped at the single serialization point used for save, copy/paste, components, templates, and undo/redo. Measured across 5 real template pages, stored size dropped ~39%, which also shrinks every undo/redo snapshot held in memory and cutsJSON.stringify/parsework per commit. - Faster page previews. On Frappe v16+, preview images render in-process via the framework's bundled headless Chromium: no external network round-trip, and local
/assetsand/filesresolve during development. Older frameworks fall back to the existingpreview_generatorservice, so nothing breaks on v15. - Combined Google Fonts request. Font requests are batched into a single call with a
preconnecttogstatic, reducing round-trips when loading fonts on published pages. - Request sequencing for component data. A
useLatestRequestcomposable ensures only the latest component data fetch is applied, avoiding stale overwrites and redundant work when props change rapidly.
For the complete list of commits, bug fixes, and refactors, see the full changelog.




