The Component Anatomy
Three parts, one file
Section titled “Three parts, one file”A Svelte component is a .svelte file with up to three sections:
<script> // 1. Logic: state, props, imports, functions. let name = $state('world');</script>
<!-- 2. Markup: HTML plus Svelte template syntax. --><h1>Hello {name}!</h1><input bind:value={name} />
<style> /* 3. Styles: scoped to THIS component by default. */ h1 { color: #ff3e00; }</style>- The
<script>block is the component’s logic: state (with runes), imported components, and functions. It runs once when the component is created. - The markup is HTML enhanced with Svelte’s template syntax —
{expressions}, blocks like{#if}, and bindings. This is what renders. - The
<style>block is CSS that, by default, applies only to this component.
There’s no boilerplate wrapper, no return, no render() — the file is the component.
flowchart LR script["script: state, props, logic"] --> compile["Svelte compiler"] markup["markup: HTML + template syntax"] --> compile style["style: scoped CSS"] --> compile compile --> comp["a component: JS + a stylesheet"]
Scoped styles by default
Section titled “Scoped styles by default”The <style> block is scoped to the component — Svelte adds a unique class to the component’s elements and rewrites your selectors to match only them. Write h1 { color: … } and it styles this component’s h1, not every h1 on the page.
<p>This paragraph is styled locally.</p><style> /* Compiles to something like p.svelte-abc123 { ... } — scoped, no leakage. */ p { font-weight: bold; }</style>You get component-local CSS with plain selectors — no naming conventions, no CSS-in-JS. When you deliberately want a global rule, you opt out with :global(...):
<style> :global(body) { margin: 0; } /* escape the scope on purpose */ .card :global(a) { color: teal; } /* scoped .card, global descendant a */</style>TypeScript and the script
Section titled “TypeScript and the script”Add lang="ts" to the script tag to write TypeScript — the compiler type-checks and strips it:
<script lang="ts"> let count: number = $state(0); function inc(): void { count++; }</script>Everything else is unchanged; runes are typed ($state<T>), and props get typed interfaces (a later lesson). Svelte’s tooling (svelte-check) type-checks .svelte files including the markup.