Dynamic Shortcodes
Turbo does not expand Dynamic Shortcodes at bake/render time for visitor-specific values. Instead, raw {type:…} expressions (and optional [dsh]…[/dsh]) become client stubs that hydrate through WordPress — same idea as xAPI actor session / Gravity Forms submit.
Requirements
Section titled “Requirements”- Plugin Dynamic Shortcodes active.
- Filter
wp2turbo/dsh/enable(default: on when the plugin is present).
Authoring
Section titled “Authoring”Write Dynamic Shortcodes as usual in Gutenberg content:
Привет, {user:display_name}!{if:{user:ID} "Вы вошли" "Гость"}- Embed another post’s body as a Turbo fragment (not raw Gutenberg markup):
{post:content@id=123}Also: {post:post_content@id=123}, {post:content-rendered@id=123}. Without @id, the expand context post is used.
Put the shortcode in its own block (not inside a paragraph with other text). Block-level embeds are hoisted out of <p> automatically, but a dedicated block is cleaner.
Blocking: embed HTML is wrapped in [data-turbo-dsh-embed]. Nested tabs/details/questions inside the embed do not gate the parent page’s «Продолжить» (requireContentComplete). Blocking gates inside the embed are initialized after hydrate.
In Custom HTML (core/html / «Произвольный код»), shortcodes inside tags stay valid — the whole element becomes one island. If the markup has width/height (or those in style), the skeleton uses them:
<img src="{user:avatar-url}" width="96" height="96" alt="">Optional WP wrapper still works as one island:
[dsh]Привет, {user:display_name}[/dsh]<script>, <style>, and HTML comments are skipped so CSS/JS braces are not wrapped.
Pipeline
Section titled “Pipeline”StaticPageRenderer→DynamicShortcodesContentProcessorparses via DSH API and emits<span data-turbo-dsh … data-turbo-dsh-post-id="…">.StaticAssetCollectorloadsdsh-expand.js+ inlinewindow.wp2turboDsh.- Client waits for
wp2turbo:xapi-actor-session(if enabled), then reads a sessionStorage cache keyed by decoded islandsrc+ viewer scope (xAPI actor email, elseguest). Cache stores{ html, assets?, hash? }; hits callensureAssetsthen apply with no network. - Misses refresh nonces via
wp2t_boot(same as GF), then batch-expand. Response may include aggregatedassets(styles/scripts for Turbo post embeds). Successful HTML (+ assets/hash) is written back to sessionStorage (max 100 entries). Cache clears when the xAPI viewer identity changes. {post:content*}: server runsStaticPageRenderer::renderFragment(content pipeline, no page shell). Fragments are object-cached bypostId + contentHash + skin + lang(stubs inside stay stubs). Limits: max depth 3, max 10 unique embed posts per expand request; identical@idbatched once. Nested stubs re-hydrate (depth ≤ 3).- Shared transport with Gravity Forms (
assets/src/transport.js): REST first; on static package path or after 405 → heartbeatdata[wp2t_dsh]. Flagwindow.wp2turboTransport.restDeadstops further REST attempts for the page. - Privileges
none(Power Shortcodes{pw:…}work as in DSH). Draft/private posts are not embedded.
Events / config
Section titled “Events / config”- Config:
window.wp2turboDsh = { restUrl, nonce, postId } - Event after hydrate:
wp2turbo:dsh-expanded(detail.cached/detail.fetched) - Expand response:
{ ok, expansions: [{ id, html, hash? }], assets?, nonce }
Assets
Section titled “Assets”- JS:
onepix/wp2lms/src/Modules/Turbo/assets/src/dsh-expand.js,dsh-cache.js - CSS stub:
.wp2turbo-dsh[aria-busy="true"]in skincomponents.css
- During Turbo render, native DSH expand on
render_block/the_content(and the[dsh]WP shortcode) is temporarily suspended so raw expressions reach the stub processor. Hydration then expands them with the live user/session. - If you rely on DSH expand outside Turbo pages, those filters stay intact after render.