Svelte components
Learning objectives
- You know the structure of Svelte components.
- You know how to build component hierarchies.
- You know how to pass properties to components.
Component structure
Components in Svelte consist of JavaScript (optional), HTML template (mandatory), and style definitions (optional).
The following example demonstrates this structure. It has JavaScript that is used to define a variable task
and a function taskTyped
. The HTML template part asks for the user to provide a task name, binds the input to the variable task
, and has a button for creating an alert about the task which, when clicked, calls the function taskTyped
. The styling part sets padding and margin for the section
element and sets the font as Comic Sans MS
.
<script>
let task = '';
const taskTyped = () => {
if (task.length === 0) {
return;
}
alert(task);
task = '';
}
</script>
<section>
<p>Provide task name</p>
<input bind:value={task} />
<button on:click={taskTyped}>Add task</button>
</section>
<style>
section {
padding: 0.5rem;
margin: 0.5rem;
font-family: 'Comic Sans MS';
}
</style>
Source code files that are used to define components should be capitalized following PascalCase to allow distinguishing components them from other files and content. The suffix of Svelte components is .svelte
. Let's assume that the above component resides in a file called TaskInput.svelte
.
Importing components
Let's assume that there is another component called Tasks.svelte
, which is in the same folder with TaskInput.svelte
. Importing components is done with the JavaScript import
command -- the Tasks.svelte
could use the TaskInput
component as follows.
<script>
import TaskInput from "./TaskInput.svelte";
</script>
<h1>Welcome to the application</h1>
<TaskInput />
Passing properties
Let's add a possibility to show a list of tasks to the application. Let's just start with showing the number of tasks, backed up by a list. For this, we define a list variable tasks
to the Tasks.svelte
and show the length of the list in the component.
<script>
import TaskInput from "./TaskInput.svelte";
let tasks = [];
</script>
<h1>Tasks</h1>
<p>Total tasks: {tasks.length}</p>
<TaskInput />
Currently, the TaskInput
component has no access to the list of tasks. We can pass functions as properties into components by defining them as arguments to the component. Let's create a function addTask
that is given a task as an argument, and pass it to the TaskInput
component.
<script>
import TaskInput from "./TaskInput.svelte";
let tasks = [];
const addTask = (task) => {
tasks = [task, ...tasks];
}
</script>
<h1>Tasks</h1>
<p>Total tasks: {tasks.length}</p>
<TaskInput addTask={addTask} />
Now, we need to adjust the TaskInput
component. Properties are taken into use with the export
command. In the following, we have adjusted the TaskInput
component so that it takes the property addTask
into use (on line 2), and then uses it when adding a task (on line 10). We've also removed the alert.
<script>
export let addTask;
let task = '';
const taskTyped = () => {
if (task.length === 0) {
return;
}
addTask(task);
task = '';
}
</script>
<section>
<p>Provide task name</p>
<input bind:value={task} />
<button on:click={taskTyped}>Add task</button>
</section>
<style>
section {
padding: 0.5rem;
margin: 0.5rem;
font-family: 'Comic Sans MS';
}
</style>
When you try the above example locally, you'll notice that the number of tasks shown in the Tasks
component changes as you add more tasks.
Why not push?
In the above example, we reassign the value of variable tasks
instead of using the push
method that lists have. Svelte monitors for changes in variables, but not within variables -- hence, we need reassign the value if we wish to see changes reflected in the UI.
While the above example demonstrates the use of functions as properties, the properties can also be simple variables. For the example, the following example outlines a simple greeting component.
<script>
export let name;
</script>
<h1>Hello, {name}!</h1>
If the component would reside in a file called Greeting.svelte
, it could be used as follows (given it is also imported).
<script>
import Greeting from "./Greeting.svelte";
</script>
<Greeting name="world" />
Similarly, it could also be passed a variable value as follows.
<script>
import Greeting from "./Greeting.svelte";
let name = "world";
</script>
<Greeting name={name} />
Passing content
It is also possible to create components so that they are given HTML content. Content given from a parent component is added using a tag <slot />
, which will be replaced with the content passed from the parent. We could, for example, adjust our Greeting
widget as follows.
<script>
export let name;
</script>
<h1>Hello, {name}!</h1>
<blockquote>
<slot />
</blockquote>
Now, content we place within the Greeting
widget when used is placed to the position of the slot
entity. The above widget would be used as follows.
<script>
import Greeting from "./Greeting.svelte";
let name = "world";
</script>
<Greeting {name}>
Once upon a time...
</Greeting>
The above <Greeting {name}>
is a shorthand for <Greeting name={name}>
.