When a component interpolates a value as text, what does the framework guarantee, and what changes with the raw-markup opt-in?
answer
- a value fills a hole, never widens one
- text position versus markup position
- the opt-in transfers responsibility
- sanitise on output, close to render
- the parsed subtree is untracked
basics
~20 sInterpolated values are set as text, so markup characters stay literal and a value can never add an element or a handler. The raw opt-in drops that guarantee: the string is parsed as markup, so it must be trusted or sanitised.
solid answer
~50 sAn interpolated value becomes **text content**, not markup. Whatever the value contains, the framework puts it where the host treats it as characters, so angle brackets and quotes appear as themselves and a value can never create an element, an attribute or an event handler. That is the default precisely because most values come from somewhere you do not control. The raw-markup opt-in reverses it: the string is handed to the host's markup parsing and whatever it describes becomes real nodes. Frameworks name that opt-in verbosely on purpose: the guarantee is gone and correctness is yours, so the string must be content you produced or a sanitiser's output. Two consequences matter: the parsed subtree is not described by your component, so the framework neither tracks nor updates it, and text escaping says nothing about a value used as an attribute such as a link target.
code
pseudocode · 5 linesstate comment = "<img src=x onerror=steal()>"
render:
text child: comment # rendered as visible characters, no element created
raw markup child: comment # parsed as markup, the element and its handler are realgo deeper
Remember that an interpolated value is shown as characters, so markup inside it is visible rather than active, and that rendering a string as real markup requires a deliberate, differently named opt-in.
Explain why text is the default, what the opt-in changes about who owns correctness, and that the injected subtree sits outside the framework's tracking entirely.
Know the responsible pattern: a maintained sanitiser running on the way out, at the render boundary, plus the recognition that attribute and target positions are separate problems.
Decide where this may appear at all. A single reviewed component that owns all rich-text rendering, with sanitisation inside it, beats an opt-in that any team may reach for.
Every component framework has the same default and the same escape hatch, and knowing why the default exists is the interview question. ## Interpolation produces text, not markup When you write an interpolated value into a template's text position, or place a value as a child in a render function's returned description, the framework sets it as **text content**. The distinction is about which parser sees the characters: - as text content, `<` and `&` are just characters and appear on screen as themselves; - as markup, the same characters are structural — they open a tag or begin an entity. Because the framework chooses the text path, the value's content cannot change the shape of the output. A value of `<b>hi</b>` renders those seven visible characters, not bold text. Whether the framework literally replaces characters with entities or simply writes to a text-setting host API depends on whether the output is being serialised — for example when rendering to a string on a server — or built as live nodes. Either way the guarantee the author sees is the same: **a value fills a hole; it never widens one.** ## What the opt-in actually changes The raw opt-in says: parse this string as markup and put the result here. Once you take it: - the string's structure becomes real nodes, including elements the framework knows nothing about; - any attribute in the string is applied, including ones that attach behaviour, which is why untrusted input here is a genuine vulnerability rather than a cosmetic bug; - the subtree is **outside** the framework's description, so it is not tracked, not diffed, and not updated when your state changes — it is replaced wholesale when the string changes; - nothing inside can be hoisted or analysed by a compiler, since there is no structure until the string is parsed; - component syntax inside the string is inert: it is markup to the host, not a description for the framework, so a child component named in it is never instantiated. | | interpolated text | raw-markup opt-in | |---|---|---| | how characters are treated | as text | as markup structure | | can the value add an element or a handler | no | yes | | who owns correctness | the framework | you | | framework tracking of the result | full | none, replaced as a unit | | compiler analysis inside | possible | impossible | ## Using the opt-in responsibly The legitimate cases are narrow: markup you generated yourself from a trusted source, or untrusted content that has passed through a real sanitiser. The two rules that matter: 1. **Sanitise on the way out, as close to rendering as possible** — not when the data arrives and not once in a database column, because the same stored value may later be used in a position with different rules, and a value sanitised for one destination is not thereby safe for another. 2. **Use a maintained sanitiser, do not write one.** Filtering markup correctly means matching a real parser's behaviour on malformed input, and hand-rolled allow-lists lose to parser quirks. 3. **Keep the opt-in in one place.** One reviewed component that owns rich-text rendering, with the sanitiser inside it, is far easier to audit than the same opt-in scattered across a dozen features, each with its own idea of what was already safe. It is also worth remembering what the text-position guarantee does *not* extend to. A value interpolated into an attribute is a different position with different rules, and a value used as a link or resource target needs its scheme validated rather than its characters escaped. Framework escaping covers the text hole it filled; the deeper theory of output positions, and the specific host APIs that turn strings into live markup, belong to the security topics that own them. ## Interview shape State the default and its reason first: text, because values usually come from outside and the framework will not let one of them change the output's structure. Then describe the opt-in as a transfer of responsibility, not a feature. Then add one thing most candidates miss — that the parsed subtree is outside the framework's tracking, so it neither updates with your state nor benefits from any compile-time analysis. If you can also say where the guarantee stops, at attribute and target positions, you are ahead of the median answer.
- Why does escaping text stop short of making an attribute position safe?Because safety depends on how the value will be interpreted there. A link or resource target is read as an address, so a scheme that executes code is dangerous no matter how the characters are escaped. Those positions need validation of the value's meaning, not of its punctuation.
- What happens to a child component named inside a raw-markup string?Nothing. The string is parsed by the host as markup, so the name becomes an unknown element and the framework never sees a request to instantiate a component. Component resolution happens over the described output, which a raw string is not part of.
- Why sanitise at render time rather than when the data is stored?Because the safe form depends on the destination. A value stored pre-escaped for one output position is wrong for another, shows up double-escaped when rendered as text, and loses its original characters. Store what the user submitted and make it safe for each position at the point of use.
- Does the raw opt-in interfere with how the framework updates the surrounding output?It does not affect its siblings, but the injected subtree is opaque. Frameworks typically treat it as one unit tied to the string: unchanged, it is left alone; changed, the whole subtree is usually discarded and re-parsed rather than updated piece by piece.
saying these in an interview costs you the question
- Thinks the raw opt-in is just a shortcut for rendering markup
- Believes escaping text also makes attribute and link positions safe
- Sanitises once on input and treats the value as safe forever
- Writes a hand-rolled markup filter instead of using a sanitiser
- Expects components named inside a raw string to be instantiated
- Assumes the framework still tracks and updates the injected subtree