Composition Over Configuration: Content Projection with Signal Queries In short: gives consumers a slot. tells you whether they used it, so you can skip the wrapper when they did not. Together they replace most configuration inputs. The previous lesson diagnosed the disease. This is the cure: build a card that provides structure and knows nothing about content. Multi-slot projection moves markup from the consumer's template into yours. With a attribute you can have several slots, each matching a CSS selector. Our card needs three regions: Slot Selector --- --- Header Body the default, unselected Footer This works, and it already removes most of the inputs. But it has a real bug: a card with no footer still renders , which has a top border and 1rem of padding. Every consumer without a footer gets a stray line and a gap. The old fix was a input — which is exactly the trap we are escaping. Detecting a filled slot with Signal queries let the component ask whether a slot was actually filled. First, give each slot a real directive rather than a bare attribute selector. This is worth doing for its own sake, and we come back to why in lesson five: Then query for them: returns a signal, so is reactive and safe to read in the template — no problems, which is exactly why the signal-based queries replaced . Two teams, one card, no library changes No footer element is rendered for the second card. No input said so — the component looked. Projected content belongs to the consumer One thing that surprises people: projected content keeps the consumer's styles, not the library's. Angular's default emulated encapsulation scopes styles by where the template was declared . The lab team's was declared in the lab team's template, so it carries the lab component's style attribute and is styled by the lab component's CSS. This is the correct behaviour and it is what makes projection safe. The card can style its own wrapper — padding, border, layout — and cannot accidentally restyle whatever gets dropped inside it. If the card genuinely needs to influence projected content, do it through inherited properties and custom properties, not descendant selectors: Chapter 6 turns those custom properties into a proper theming contract. Fallback content A slot can have a default that renders when nothing is projected: Useful for genuine defaults. Do not use it to smuggle configuration back in — if the fallback needs to vary by consumer, it was never a fallback. for wrapped content Projection matches against the element as written in the consumer's template. That breaks when the consumer wraps a slot in a structural block: Actually, that case is fine — does not create a matched element. The problem is and : Worth knowing before you spend twenty minutes wondering why a slot is empty. "But I thought content projection made components harder to use" There is something genuine here, and it is not the projection itself — it is discoverability. With inputs, the tooling helps you. Type and the Angular Language Service lists , , . You can learn the API by pressing a key. With a bare attribute selector, you get nothing. is an HTML attribute as far as the editor is concerned. A consumer has no way to discover that exists, and a typo — — fails silently. The content lands in the default slot, the header looks wrong, and there is no error anywhere. That is a real cost, and it is why this lesson introduced and as directives rather than using bare selectors. Because they are directives: They appear in autocomplete once imported. A typo is a compile error under , not a silent misplacement. They are exported from , so they are documented API. They give the component something typed to query, instead of a magic string. Lesson five takes this further into the full compound component pattern. For now, the comparison: Bare attribute selector Slot directive --- --- --- Autocomplete None Yes Typo behaviour Silent — lands in wrong slot Compile error Discoverable from the import No Yes Queryable by the component Only by template ref var By class Documented in No Yes So the belief is right about the symptom and wrong about the cause. Projection is not harder to use than configuration — undocumented, untyped projection is. Give the slots types and the objection disappears, along with the 384 states. Cheat sheet Do Don't --- --- for regions inputs Declare slots as directives Bare attribute selectors + to skip empty wrappers input Let projected content keep the consumer's styles Style projected content with descendant selectors Expose custom properties for shared visual traits from either side on Wonder why the slot is empty Export slot directives from Leave them internal Recap Multi-slot projection replaces content inputs. The consumer supplies markup; the component supplies structure. tells you whether a slot was filled , so wrappers only render when they have content. Signal queries are reactive and safe to read in the template. Projected content keeps the consumer's…