This episode dissects Svelte's architecture: how the compiler turns reactive declarations into imperative code, the structure of .svelte files, reactive declarations and stores, lifecycle hooks, and the scoped CSS mechanism. This is the foundation every following episode relies on.

In episode 1 you learned that Svelte is a compiler. Episode 2 goes deeper: what actually happens when the compiler runs, and how a .svelte component is structured. If you understand this architecture, Svelte's seemingly magical syntax reveals itself as a systematic code transformation.
Svelte's reactivity is not magic. It is the result of careful compilation: the compiler reads your declarations, tracks dependencies between variables, and generates direct, efficient DOM update code. Understanding this flow helps you write well-targeted components and avoid common mistakes.
This episode covers how the compiler works, component structure, reactive declarations, lifecycle hooks, and scoped CSS. We will also touch on stores as a cross-component state mechanism, which episode 6 covers in full.
You write components declaratively — "show this value" — without telling the browser how to do it. The Svelte compiler reads that declaration and turns it into imperative code the browser understands directly.
<script>
let pesan = "Halo Svelte"
</script>
<h1>{pesan}</h1>let pesan = "Halo Svelte" is a state declaration. At build time, the compiler sees that <h1>{pesan}</h1> depends on pesan, then generates an update function that writes the value of pesan straight into the <h1> element when it changes. No diffing at runtime.
Because the compiler already knows where every dependency lives, DOM updates happen with precision. When pesan changes, only the <h1> element is updated — not the entire subtree. This is the fundamental difference from React and Vue, which compare virtual DOMs in the browser.
Imagine a list of 10,000 items. In React, the framework compares the old and new virtual trees to find differences. In Svelte, the compiler generates code that adds, removes, or changes only the elements that actually changed. The effect is most noticeable in applications with many state updates.
The compiler analyzes every variable usage inside a component and builds a dependency graph. When a variable changes, the compiler knows exactly which derived variables and DOM elements must be updated. This graph is built at compile time, so there is no runtime computing it in the browser.
With runes in Svelte 5, the compiler understands syntax like $state and $derived explicitly, making this tracking clearer and usable anywhere — including inside ordinary .js modules, not just components.
A .svelte file consists of three blocks: script, markup, and style. Markup is the core of the component; script provides the logic; style controls the appearance.
<script>
let nama = "Svelte"
</script>
<div class="kartu">
<h1>Halo {nama}</h1>
</div>
<style>
.kartu {
padding: 1rem;
border-radius: 8px;
}
</style>{nama} inside the markup is interpolation: its value is inserted into the text. The <style> block looks like ordinary CSS, but it has a major special feature we will cover in the scoped CSS section.
There are two kinds of script in Svelte 5: the regular <script> for component instances, and <script module> for code that runs once when the module loads — ideal for cross-instance state. Use <script module> for constants, helpers, or stores shared between instances of the same component.
Before runes, Svelte used $: to declare derived values. This syntax still works in Svelte 5 in legacy mode:
<script>
let harga = 1000
$: pajak = harga * 0.11
</script>
<p>Total: {harga + pajak}</p>$: pajak = harga * 0.11 means "every time harga changes, recalculate pajak". In modern runes mode, the equivalent expression is written let pajak = $derived(harga * 0.11). The concept is the same: a value computed automatically from other values.
A store is an object that holds state and can be subscribed to from anywhere. When the state changes, all subscribers receive the new value. You will use writable, readable, and derived from svelte/store extensively.
import { writable } from "svelte/store"
export const hitung = writable(0)writable(0) creates a store with an initial value of 0. In episode 6 we will cover stores completely: when to use them, how to write custom stores, and when runes $state are a better fit.
Svelte components go through a life cycle: they are created, updated, and destroyed. Lifecycle hooks are used to capture those moments:
onMount — runs after the component is mounted to the DOM; the ideal place for data fetching.beforeUpdate — runs right before the DOM is updated.afterUpdate — runs right after the DOM is updated.onDestroy — runs when the component is destroyed; the place to clean up timers, listeners, and subscriptions.tick — waits until a state update has finished rendering.<script>
import { onMount, onDestroy } from "svelte"
let detik = 0
let timer
onMount(() => {
timer = setInterval(() => (detik += 1), 1000)
})
onDestroy(() => clearInterval(timer))
</script>
<p>Sudah berjalan {detik} detik</p>import { onMount, onDestroy } from "svelte" pulls the hooks from the svelte package. onDestroy(() => clearInterval(timer)) prevents memory leaks by clearing the timer when the component disappears — a mandatory habit for production components.
CSS inside a component's <style> block is automatically scoped: the compiler adds a hash class to every affected element, so styles do not leak into other components. This solves the global CSS problem without needing convoluted class names like BEM.
<style>
.kartu {
background: #fff;
}
</style>The .kartu {: ... } above only applies to elements with class kartu inside this component. Behind the scenes, the compiler rewrites the selector to something like .kartu.svelte-abc123. No collisions between components.
Sometimes you genuinely want to target elements outside the component — for example, styling a library element. Use :global:
:global(.modal-backdrop) {
background: rgba(0, 0, 0, 0.5);
}:global(.modal-backdrop) keeps that selector unscoped. Use it sparingly — too much :global means you are fighting the very system that is supposed to help you.
Key takeaways:
.svelte file consists of script, markup, and style; use <script module> for shared logic.onMount, afterUpdate, onDestroy) manage the important moments of a component.:global only when you truly need it.In the next episode 3 we will start a real Svelte project — creating an application with npm create svelte@latest, understanding the src/routes, src/lib, and static structure, running the dev server with hot reload, and setting up TypeScript, ESLint, and Prettier. See you in the next episode!