Added
- Runtime layout switching —
self.set_layout(path)(v0.6.0) — Phoenix 1.1 parity. An event handler can swap the surrounding layout template (nav, sidebar, footer, wrapper markup) without a full page reload: inner state — form values, scroll position, focused element,dj-hookbookkeeping, third-party-widget references — is fully preserved because the live[dj-root]element is physically moved from the current body into the new layout rather than re-created. Server side: newLayoutMixininpython/djust/mixins/layout.pycomposed into theLiveViewbase, queuing at most one pending path (last-write-wins). WebSocket consumer: new_flush_pending_layout()wired at all nine_flush_page_metadatacall sites; renders the layout template with the view's currentget_context_data()and emits a{"type": "layout", "path": ..., "html": ...}frame. Graceful degradation:TemplateDoesNotExistor any render exception logs a warning and leaves the WS intact. Client side: newstatic/djust/src/40-dj-layout.jsmodule registered for thelayoutWS frame — finds the[dj-root]/[data-djust-root]inside the incoming HTML, splices in the live root node, swapsdocument.body, and fires adjust:layout-changedCustomEvent ondocument. Handles missing-root payloads and empty HTML gracefully. Tests: 12 Python cases intests/unit/test_layout_switching.py(mixin, consumer emit/noop/missing-template/no-mixin/view-none, LiveView composition) + 6 JSDOM cases intests/js/dj_layout.test.js(root-identity preservation, CustomEvent dispatch, malformed-payload refusal, empty-html noop,[dj-root]fallback, global export). Full user guide atdocs/website/guides/layouts.md(linked from_config.yamlandindex.md). Known limitation:<head>tags are not merged — if a layout needs new stylesheets, add them to the initial layout's<head>. (python/djust/mixins/layout.py,python/djust/mixins/__init__.py,python/djust/live_view.py,python/djust/websocket.py,python/djust/static/djust/src/03-websocket.js,python/djust/static/djust/src/40-dj-layout.js)