How do layouts and partials compose one page in a server-side template engine, and what does each mechanism cost?
answer
- two directions: frame outside, fragment inside
- named holes filled by the page
- ambient scope versus explicit parameters
- shared fragments need a contract
- a fragment that queries repeats per row
basics
~20 sLayouts compose top-down: a frame declares named holes a page fills. Partials compose bottom-up: a fragment renders inside a caller, taking either the caller's whole context or an explicit parameter list. Ambient partials cost reusability.
solid answer
~50 sThere are two composition directions. A **layout** is the outer frame — the page shell with named blocks or slots — and a page template declares which layout it uses and supplies content for those blocks; the engine renders the combination as one document. A **partial** is the inner direction: a fragment template rendered at a point inside another template, either inheriting the caller's full variable scope (ambient) or receiving an explicit parameter list (a call signature). Ambient partials are quick to write and quietly couple the fragment to whatever the caller happened to put in the model, so reusing one from a second page breaks it. Explicit parameters cost a few lines and make the fragment reusable and testable. Deep layout chains and partial trees also make it harder to answer "where did this value come from".
go deeper
Recall the two mechanisms: a layout is the shell with named holes a page fills, and a partial is a smaller template rendered inside another one.
Explain what scope a fragment sees — the caller's whole context or a declared parameter list — and why that choice decides whether the fragment can be reused from a second page.
Show the production angle: data loading inside fragments repeating per row and escaping the handler's time budget, runtime-computed fragment names defeating caching, and deep chains making value provenance unanswerable.
Take a position on where the fragment boundary should sit for a codebase — component contracts versus convenience includes, and how fragments shared with an incremental-update path avoid drifting from the full-page render.
A page of any size is not one template. Engines give two composition mechanisms that pull in opposite directions, and most confusion about server-side templating comes from mixing them without noticing which one is in play. ## Layouts: composing from the outside in A layout is the page frame — document shell, header, navigation, footer — with **named holes** in it: blocks, sections, slots, whatever the engine calls them. A page template says which layout it belongs to and provides content for some of those holes; holes it leaves alone keep the layout's default. Two execution styles exist: - **The child drives.** Rendering starts from the page template, which declares its parent; the engine renders the parent and substitutes the child's blocks as it reaches them. - **The frame drives.** Rendering starts from the layout, which pulls the page's rendered body in at a designated point. The observable difference shows up in ordering questions: whether the page body is rendered before or after the surrounding frame decides whether the body can still contribute something the frame uses — a title, a set of page-specific styles, a breadcrumb. Engines that render the body first can let it push values upward; engines that render the frame first need those values supplied in the model instead. ## Partials: composing from the inside out A partial is a template rendered at a point inside another template. The question that decides everything about it is **what variable scope it sees**. | Partial style | What it can read | Strength | Cost | |---|---|---|---| | Ambient context | the caller's entire scope plus framework-supplied values | nothing to wire up; fastest to write | silently depends on names the caller happens to provide | | Explicit parameters | only what the call passes in | reusable, testable, its contract is readable | more typing, and every call site must be updated when it changes | | Self-loading fragment | fetches or computes its own data at render time | callers stay ignorant of what it needs | hides work inside rendering, and repeats per row when looped | Ambient is the default in most engines because it is convenient, and it is a perfectly good choice for a fragment that exists only to split one long page into readable files. It becomes a problem the moment the fragment is reused: the second caller does not supply `currentFilter`, the fragment renders empty or raises, and the failure appears in a template rather than in code. A shared fragment should take parameters, because that is what turns it from a copy-paste unit into a component with a contract. The self-loading fragment deserves its own warning. A partial that goes and fetches what it needs looks elegant and, rendered inside a loop, performs one lookup per row — the rendering-layer form of the repeated-query problem. Worse, the work happens after the handler has returned, which is usually after any transaction or timeout budget the handler was operating under, and often after the response has begun. ## What composition costs 1. **Provenance.** With a three-deep layout chain and nested fragments, answering "where is this value set?" means walking the chain. Shallow hierarchies — a base layout, at most one specialised layout, then pages — keep that answerable. 2. **Lookup and cache pressure.** Every included fragment is its own resolution and its own compiled-template cache entry. That is cheap, but a loop that includes a fragment per row multiplies the per-render bookkeeping, and a fragment name computed at runtime can defeat caching entirely. 3. **Inherited settings.** Escaping mode, locale and strictness generally come from engine configuration and flow into fragments regardless of who called them. A fragment cannot assume the caller's settings, and a call-site decision about unescaped output does not travel into the fragment it calls. 4. **Duplication that composition does not fix.** Splitting a page into fragments removes repeated markup but not repeated model preparation; if every page that includes a fragment must compute the same three values first, that belongs in shared code, not in every handler. ## Practical rules that hold generically - Give any fragment used by more than one page an explicit parameter list, and treat that list as its public interface. - Keep layouts shallow and give blocks names that say what they are for rather than where they sit. - Let the handler assemble the data; let templates arrange it. A fragment that queries is a fragment that will surprise someone under load. - Name fragments from a fixed set, never from request input, so resolution and caching stay bounded and predictable. - Where a fragment is also the unit you re-render for an incremental page update, define it once and render it from both the full-page path and the fragment path, so the two cannot drift.
- When is an ambient-context partial the right choice?When the fragment exists only to split one long page into readable pieces and will never be rendered from anywhere else. It has exactly one caller, so the implicit dependency on that caller's model costs nothing and the wiring would be noise. The moment a second page wants it, convert it to explicit parameters rather than making the new caller reproduce the first one's model.
- Why does it matter whether the page body renders before or after the surrounding frame?It decides whether the body can contribute values the frame uses, such as a document title or page-specific assets. If the body renders first, the engine can collect what it pushed upward and the frame can read it. If the frame renders first, those values must already be in the model, which usually means the handler supplies them rather than the template deciding.
- What goes wrong when a fragment loads its own data?The work moves out of the handler and into rendering: one lookup per row when the fragment is inside a loop, execution after the handler's transaction and time budget have ended, and failures that surface mid-response when the status is already sent. Keep data assembly in the handler and pass results in, so cost and error handling stay where they can be controlled.
saying these in an interview costs you the question
- Assumes a fragment can always read whatever the calling page had in scope
- Reuses an ambient fragment on a new page without supplying its implicit names
- Puts data loading inside a fragment rendered once per row
- Thinks nesting fragments deeper removes duplication of model preparation
- Believes an unescaped-output decision at the call site carries into the fragment