Course outline

Svelte / Basic: reactive components and ownership / Lesson 4

Conditional lists, keys and snippet composition

Preserve item identity and deliberate markup reuse without confusing rendering with source validation or authority.

About 4 min readContent reviewed
On this page

Lesson goals

  • Explain if/each branches and stable keyed item identity.
  • Use snippets for clear local composition and avoid untrusted raw HTML.

List position is not necessarily object identity

An each block renders a collection, while a keyed each block tells Svelte which logical item a rendered region belongs to across insertions, deletion and reordering. A stable course ID is usually more appropriate than the current array index when rows contain local state or editable inputs. Keys must be unique within the relevant list and stable for the intended item lifetime. A key does not authorize access or validate source fields. Keep sorting and filtering rules in the domain/view model and test what happens when the same item moves rather than only when the first static list appears.

Make reusable markup explicit with a snippet

The fragment defines one card snippet and renders it inside a keyed list. The snippet receives a course parameter rather than reaching into unrelated global state. It is local markup composition, not an independent authenticated endpoint or persistent record. A full component may be more appropriate when lifecycle, reusable behavior or a public prop API is needed. The code assumes courses is supplied as validated public input. No compiler or DOM was used, so item preservation and snippet rendering remain untested framework behavior rather than measured output.

svelte Illustrative keyed list and local snippet
<script>
  let { courses } = $props();
</script>
{#snippet card(course)}
  <li><h2>{course.title}</h2></li>
{/snippet}
<ul>
  {#each courses as course (course.id)}
    {@render card(course)}
  {/each}
</ul>

course.id is the declared item identity. No compiler checked duplicate keys or rendered the snippet.

Model empty, pending and failed data intentionally

Conditional markup should distinguish a successful empty result from unavailable data or a pending source. An empty list is not evidence that a private course does not exist, and a loading branch must not reveal protected structure before policy permits it. Await blocks and asynchronous rendering have versioned server behavior; do not assume a client loading pattern proves SSR output. Keep expected domain states explicit and let safe unexpected failures follow the applicationโ€™s error contract. This makes accessible status messages and recovery actions meaningful instead of showing the same blank region for every outcome.

Reject ambiguous source identity before rendering

The local validator checks that synthetic rows have distinct string IDs. It rejects duplicates before a framework list exists. That is a source identity rule, not an implementation of Svelte keyed reconciliation. Real data may use another validated identifier scheme and require tenant scope. The test intentionally does not guess how the DOM would recover from an invalid key set. A successful ID check also says nothing about title safety, row ordering or current eligibility; keep those checks in their own contracts rather than treating unique IDs as complete source validation.

javascript Complete offline list-identity check
function uniqueIds(rows) {
  const ids=rows.map(row=>row.id);
  return ids.every(id=>typeof id==="string") && new Set(ids).size===ids.length;
}
console.assert(uniqueIds([{id:"c1"},{id:"c2"}]));
console.assert(!uniqueIds([{id:"c1"},{id:"c1"}]));
console.assert(!uniqueIds([{id:7}]));

Distinct text IDs pass; duplicate and wrong-type IDs fail. No Svelte reconciliation or DOM state is tested.

Keep escaped text and raw HTML as different capabilities

Ordinary text interpolation and raw HTML insertion have different trust requirements. Use text for untrusted titles; raw HTML requires a reviewed content policy and sanitization appropriate to its origin and output context. Do not build a homegrown string replacement and call it a complete sanitizer. Snippet reuse also needs clear parameter ownership and accessible structure. Test reordering with edited rows, empty results and safe error output in a real isolated component/browser environment. This course executes only the ID validator; conditions, snippet compilation, keyed updates, escaping and raw-HTML behavior were not exercised. Its scope is deliberately narrower than a rendered-list security claim.

Common errors and fixes

Edited row state follows the wrong item after sorting

Why it happens
Array position was used where stable domain identity was required.
How to fix it
Use unique stable keys and preserve the intended row-state contract.
Prevent it next time
Test reorder and deletion after user editing.

Random keys reset state on every update

Why it happens
Key generation obscured rather than represented item identity.
How to fix it
Derive keys from validated stable object IDs.
Prevent it next time
Retain state-preservation fixtures across list changes.

Raw HTML displays untrusted executable content

Why it happens
Text and markup trust boundaries were conflated.
How to fix it
Prefer escaped text or a reviewed sanitization/content pipeline.
Prevent it next time
Review actual output contexts rather than a string-only check.

Check your understanding

1. Does uniqueIds prove the displayed list is correct?

Show worked solution

No. It checks one declared source invariant: distinct text identities. It does not test sort order, eligibility, safe titles, snippet parameters or Svelte DOM reconciliation. Keep that evidence and separately exercise the compiled list with insert, reorder, deletion and edited row state in an authorized isolated browser test.

2. Why should a successful empty list differ from a source failure?

Show worked solution

An empty result means the permitted query completed without matching items; an unavailable source does not establish that fact. Collapsing them can mislead users and hide faults. Preserve safe domain states and explicit recovery behavior without exposing private source messages.

Key takeaways

  • Keys express stable item identity rather than array position.
  • Reusable markup still needs explicit data and trust contracts.

Official references