Split out of #2533 and #3216. Both reporters hit the same documentation gap from different directions, and both waited a long time for an answer that should have been a docs link.
What's missing
1. Rendering a framework component as step content.
StepText accepts an HTMLElement, and shepherd-text.ts appends it directly rather than serialising it — so you can mount a live Vue/React/Svelte component to a detached node and hand that node to text, keeping reactivity and event handlers intact. Nothing in the docs says this. #2533 asked for it in Dec 2023, was told "pass HTML", asked for a demo, and never got one.
Worth documenting alongside it: the dialog chrome is Shepherd's own DOM, but everything inside is opt-out — no title/cancelIcon means no header, an empty buttons array means no footer, and arrow: false drops the arrow. With those off a component owns the entire body, which is what people asking this question actually want.
2. Why custom footer/DOM injection has to happen in when.show.
The step element is torn down and rebuilt on every show (_teardownElements() / _setupElements()), and createShepherdFooter builds a fresh <footer> each time. So a one-time DOM injection cannot survive a step change. #3216 spent a retry loop and a setTimeout ladder working around this without ever being told the element is simply recreated.
Two sharp edges belong in the same section:
- Inside
when.show, this is the Step — an arrow function silently breaks this.getElement().
- A per-step
when replaces defaultStepOptions.when wholesale (shallow Object.assign in _setOptions).
- Scope queries with
this.getElement() rather than document.querySelector, which returns the first match in document order and will find a previous step's node, since hidden steps stay attached.
Suggested home
docs-src/src/content/docs/recipes/cookbook.md already demonstrates the when.show pattern for progress indicators without explaining why it must be that hook — extending that recipe and adding a "custom content / framework components" entry would cover both.
Split out of #2533 and #3216. Both reporters hit the same documentation gap from different directions, and both waited a long time for an answer that should have been a docs link.
What's missing
1. Rendering a framework component as step content.
StepTextaccepts anHTMLElement, andshepherd-text.tsappends it directly rather than serialising it — so you can mount a live Vue/React/Svelte component to a detached node and hand that node totext, keeping reactivity and event handlers intact. Nothing in the docs says this. #2533 asked for it in Dec 2023, was told "pass HTML", asked for a demo, and never got one.Worth documenting alongside it: the dialog chrome is Shepherd's own DOM, but everything inside is opt-out — no
title/cancelIconmeans no header, an emptybuttonsarray means no footer, andarrow: falsedrops the arrow. With those off a component owns the entire body, which is what people asking this question actually want.2. Why custom footer/DOM injection has to happen in
when.show.The step element is torn down and rebuilt on every show (
_teardownElements()/_setupElements()), andcreateShepherdFooterbuilds a fresh<footer>each time. So a one-time DOM injection cannot survive a step change. #3216 spent a retry loop and asetTimeoutladder working around this without ever being told the element is simply recreated.Two sharp edges belong in the same section:
when.show,thisis the Step — an arrow function silently breaksthis.getElement().whenreplacesdefaultStepOptions.whenwholesale (shallowObject.assignin_setOptions).this.getElement()rather thandocument.querySelector, which returns the first match in document order and will find a previous step's node, since hidden steps stay attached.Suggested home
docs-src/src/content/docs/recipes/cookbook.mdalready demonstrates thewhen.showpattern for progress indicators without explaining why it must be that hook — extending that recipe and adding a "custom content / framework components" entry would cover both.