Component Creation and Usage
One of Svelte’s core strengths is its component system, which enables the creation of reusable UI blocks, each with its own state and behavior. Components can be nested to form complex UI structures while maintaining clear and modular code.
Basic Component Structure
A basic Svelte component typically includes the following parts:
<script>Tag: Defines the component’s logic, including variables, functions, and imports of other modules.<style>Tag: Specifies the component’s styles, which can be local or global.- Template: Defines the component’s HTML structure.
<!-- Basic component structure -->
<script>
export let message = 'Hello, World!';
</script>
<style>
.container {
color: blue;
}
</style>
<div class="container">
<p>{message}</p>
</div>
Prop Passing
Components can accept externally passed data, known as props, declared using export let. Props can be any data type, including strings, numbers, objects, and arrays.
<!-- Component with props -->
<script>
export let name = 'World';
</script>
<p>Hello, {name}!</p>
To use a child component in a parent component, pass props like this:
<!-- Parent component using child component -->
<script>
import Greeting from './Greeting.svelte';
</script>
<Greeting name="Alice" />
Event Handling
Components can bind event handlers using the on:eventName syntax. When the event is triggered, the handler function is called.
<!-- Component with event handling -->
<script>
export let name = 'World';
function handleClick() {
console.log(`Clicked by ${name}`);
}
</script>
<button on:click={handleClick}>Click me</button>
State Management
Components can manage local state using let or shared state using Svelte’s stores.
<!-- Component with state management -->
<script>
let count = 0;
function increment() {
count += 1;
}
</script>
<button on:click={increment}>Increment</button>
<p>Count: {count}</p>
Component Organization and Reuse
To maintain project maintainability, organize components by functionality or page structure. For example, create a top-level component for each page, then use smaller child components to build the UI.
<!-- Organizing components -->
<!-- Layout.svelte -->
<script>
import Header from './Header.svelte';
import Footer from './Footer.svelte';
</script>
<Header />
<main>
<!-- Page content here -->
</main>
<Footer />
<!-- Header.svelte -->
<script>
export let title = 'My App';
</script>
<header>
<h1>{title}</h1>
</header>
<!-- Footer.svelte -->
<footer>
<p>© 2023 My App</p>
</footer>
Code Analysis
Let’s analyze the creation and usage of Svelte components through a comprehensive example:
<!-- A comprehensive example of creating and using components -->
<!-- App.svelte -->
<script>
import Greeting from './Greeting.svelte';
import Counter from './Counter.svelte';
let name = 'Alice';
</script>
<main>
<Greeting name={name} />
<Counter />
</main>
<!-- Greeting.svelte -->
<script>
export let name = 'World';
function handleClick() {
console.log(`Clicked by ${name}`);
}
</script>
<button on:click={handleClick}>Click me</button>
<p>Hello, {name}!</p>
<!-- Counter.svelte -->
<script>
let count = 0;
function increment() {
count += 1;
}
</script>
<button on:click={increment}>Increment</button>
<p>Count: {count}</p>
In this example:
App.svelteis the top-level component, containing two child components:GreetingandCounter.Greetingaccepts anameprop and includes a click event handler.Countermaintains a localcountstate with an increment button.
Slots and Scoped Slots
Svelte’s slot mechanism is a key tool for component communication and content distribution, allowing parent components to inject dynamic content into child components. Scoped slots further enhance this by enabling child components to pass data to the parent, allowing content to be dynamically generated based on the child’s state.
Slots
Slots allow parent components to pass content to child components. By default, a Svelte component can include an unnamed slot to receive content from the parent.
<!-- Parent component -->
<script>
import Child from './Child.svelte';
</script>
<Child>
This is some content.
</Child>
<!-- Child component -->
<slot></slot>
Here, the <slot> tag in the Child component displays the content passed by the parent.
Named Slots
In addition to unnamed slots, Svelte supports named slots, allowing the parent to inject content into specific locations in the child component.
<!-- Parent component -->
<script>
import Child from './Child.svelte';
</script>
<Child>
<div slot="header">Header Content</div>
<div slot="footer">Footer Content</div>
</Child>
<!-- Child component -->
<div class="header"><slot name="header"></slot></div>
<div class="content">Some content here</div>
<div class="footer"><slot name="footer"></slot></div>
Here, the parent injects content into the header and footer slots of the child component.
Scoped Slots
Scoped slots allow the child component to pass data to the parent, enabling the parent to generate content dynamically based on the child’s state.
<!-- Parent component -->
<script>
import Child from './Child.svelte';
</script>
<Child let:item>
<p>{item.name}</p>
<p>{item.description}</p>
</Child>
<!-- Child component -->
<script>
let item = { name: 'Apple', description: 'A fruit' };
</script>
<slot {item}></slot>
In this example, the Child component passes the item object to the parent via let:item, allowing the parent to use it to generate content.
Code Case Study
Let’s analyze Svelte’s slots and scoped slots through a comprehensive example:
<!-- A comprehensive example of slots and scoped slots -->
<!-- App.svelte -->
<script>
import ProductCard from './ProductCard.svelte';
</script>
<ProductCard>
<div slot="header">Featured Products</div>
<div slot="footer">End of products</div>
</ProductCard>
<!-- ProductCard.svelte -->
<script>
let product = { name: 'Smartphone', price: '$999' };
</script>
<div class="product-card">
<div class="header"><slot name="header"></slot></div>
<div class="product-info">
<h2>{product.name}</h2>
<p>{product.price}</p>
</div>
<div class="footer"><slot name="footer"></slot></div>
</div>
<!-- Usage of scoped slot -->
<ProductCard>
<div slot="header">Featured Products</div>
<div slot="footer">End of products</div>
<div slot="product" let:product>
<h2>{product.name}</h2>
<p>{product.price}</p>
</div>
</ProductCard>
<!-- ProductCard.svelte -->
<script>
let products = [
{ name: 'Smartphone', price: '$999' },
{ name: 'Laptop', price: '$1499' }
];
</script>
<div class="product-card">
<div class="header"><slot name="header"></slot></div>
{#each products as product}
<slot name="product" {product}></slot>
{/each}
<div class="footer"><slot name="footer"></slot></div>
</div>
In this example:
- The
ProductCardcomponent includes three slots:header,footer, andproduct. - The parent
App.svelteinjects static content into theheaderandfooterslots. - Using the
productscoped slot, the parent accesses the child’sproductdata to dynamically generate product information.
Component Communication
In Svelte applications, component communication is essential for building complex user interfaces. Svelte provides several mechanisms to facilitate this, including props, events, stores, and context.
Communication via Props
Props are data passed from a parent component to a child component, representing the most direct and common communication method.
<!-- ParentComponent.svelte -->
<script>
import ChildComponent from './ChildComponent.svelte';
</script>
<ChildComponent parentData="Hello from parent!" />
<!-- ChildComponent.svelte -->
<script>
export let parentData;
</script>
<p>{parentData}</p>
Communication via Events
Events are messages sent from a child component to a parent. The child dispatches events, and the parent listens and responds to them.
<!-- ParentComponent.svelte -->
<script>
import ChildComponent from './ChildComponent.svelte';
function handleChildEvent(event) {
console.log('Received event from child:', event.detail);
}
</script>
<ChildComponent on:childEvent={handleChildEvent} />
<!-- ChildComponent.svelte -->
<script>
import { createEventDispatcher } from 'svelte';
const dispatch = createEventDispatcher();
function dispatchEvent() {
dispatch('childEvent', { message: 'Hello from child!' });
}
</script>
<button on:click={dispatchEvent}>Dispatch Event</button>
Communication Using Stores
Svelte’s stores provide a mechanism for sharing state across components, with three types: readable, writable, and derived.
// store.js
import { writable } from 'svelte/store';
export const sharedState = writable({ count: 0 });
<!-- ComponentA.svelte -->
<script>
import { sharedState } from './store.js';
function increment() {
sharedState.update(state => ({ ...state, count: state.count + 1 }));
}
</script>
<button on:click={increment}>Increment</button>
<!-- ComponentB.svelte -->
<script>
import { onMount, onDestroy } from 'svelte';
import { sharedState } from './store.js';
let state;
onMount(() => {
state = sharedState.subscribe(value => {
console.log('State changed:', value);
});
});
onDestroy(() => {
state();
});
</script>
<p>{$sharedState.count}</p>
Context
Introduced in Svelte 3.23.0, context provides a way to pass data through the component tree without explicitly passing props at every level.
<!-- App.svelte -->
<script>
import { setContext } from 'svelte';
import ChildComponent from './ChildComponent.svelte';
const myContext = 'default value';
setContext('myContext', myContext);
</script>
<ChildComponent />
<!-- ChildComponent.svelte -->
<script>
import { getContext } from 'svelte';
const contextValue = getContext('myContext');
</script>
<p>{contextValue}</p>
Comprehensive Example
Let’s combine the above communication methods in a more complex example:
<!-- App.svelte -->
<script>
import { writable } from 'svelte/store';
import { setContext } from 'svelte';
import ChildComponent from './ChildComponent.svelte';
const appState = writable({ message: 'Welcome to Svelte!' });
setContext('appState', appState);
function handleChildEvent(event) {
appState.set({ message: event.detail.message });
}
</script>
<ChildComponent on:childEvent={handleChildEvent} />
<!-- ChildComponent.svelte -->
<script>
import { getContext, createEventDispatcher } from 'svelte';
const appState = getContext('appState');
const dispatch = createEventDispatcher();
function dispatchEvent() {
dispatch('childEvent', { message: 'Hello from child!' });
}
</script>
<p>{$appState.message}</p>
<button on:click={dispatchEvent}>Dispatch Event</button>
In this example:
- A
writablestore (appState) shares state between components. - The child component dispatches a
childEventto the parent. - Context passes the
appStatestore through the component tree.



