Skip to content

The Component Anatomy

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"]
The three parts of a .svelte file

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>

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.

What are the three parts of a .svelte file?
By default, where does a component's `<style>` block apply?
How do you make a style rule apply globally on purpose?
How do you use TypeScript in a Svelte component?