Client-Side Pages, Components, and Interactivity

Components and Properties


Learning Objectives

  • You know how to pass properties to components.
  • You know of the relationship between pages and components.

In the last chapter, when learning about dynamic route parameters, we learned that properties are accessed in Svelte pages using the $props() function.

In +page.svelte, the data property read via $props() comes from the page’s load() function. Regular child components do not get that data automatically: instead, you must pass properties to them explicitly.

Here, we look into passing properties to components.

Component properties

Properties allow passing data from a parent component to a child component. This is useful when we want to reuse a component in different contexts, but with different data.

Syntax and default values

The syntax for declaring properties is as follows — below, we declare a single property called name:

let { name } = $props();

If there are multiple properties, they are separated using commas — below, we declare two properties, name and yearOfBirth:

let { name, yearOfBirth } = $props();

If we want to declare a property with a default value, we can use the following syntax:

let { name = "John Doe" } = $props();

Using properties in a component

The properties can be used like any other variable in the component. For example, if we have a property called name, we can use it in the HTML template of the component as follows:

<script>
  let { name } = $props();
</script>

<h1>Hello, {name}!</h1>

Passing properties

The properties are passed to a component when the component is used. For example, if we have a component called Greeting.svelte that has a property called name, we can pass the property as follows:

<script>
  import Greeting from "$lib/components/Greeting.svelte";
</script>

<Greeting name="Alice" />

Properties can also be passed using variables, and they can also be expressions. For example:

<script>
  import Greeting from "$lib/components/Greeting.svelte";

  let userName = "Bob";
  let isMorning = true;
</script>

<Greeting name={userName} />
<Greeting name={isMorning ? `tired ${userName}` : userName} />
Loading Exercise...

Components and pages

The relationship between components and pages is that pages are often built using components. A page can be thought of as a component that is rendered when the user navigates to a specific route. Components, on the other hand, are reusable pieces of code that can be used in multiple places in the application.

Pages in Svelte are also components, but they are special components that are rendered when the user navigates to a specific route.

When constructing an application, the page should be kept as simple as possible, and the logic should be mainly in components. This allows for better separation of concerns and makes the code easier to maintain.

Loading Exercise...

Let’s continue with the example we started in the last chapter, where we created a dynamic route for books and chapters. Here, we will create components for displaying the list of books and the details of a specific book, including its chapters.

Listing books

BookList component

First, create a folder called books to the folder src/lib/components. This folder will contain components related to books. Then, create a component called BookList.svelte and place it in the src/lib/components/books folder. This component will be used to display a list of books, each with a link to the book’s page.

Place the following content to the BookList.svelte file.

<script>
  let books = $state([
    { id: 1, name: "HTML for Hamsters" },
    { id: 2, name: "CSS: Cannot Style Sandwiches" },
    { id: 3, name: "JavaScript and the Fifty Shades of Errors" },
  ]);
</script>

<ul>
  {#each books as book}
    <li>
      <a href={`/books/${book.id}`}>{book.name}</a>
    </li>
  {/each}
</ul>

Books page

Next, modify the page src/routes/books/+page.svelte to use the BookList component — if you did not follow the example from the last part, create the file. The page +page.svelte should now look like this:

<script>
  import BookList from "$lib/components/books/BookList.svelte";
</script>

<h1>Books</h1>

<BookList />

Now, when you visit the path /books, you should see a list of books, each with a link to the book’s page. This is shown in Figure 1 below.

Fig 1. -- The path /books shows a list of books, each with a link to the page of a specific book.

Fig 1. — The path /books shows a list of books, each with a link to the page of a specific book.

Showing an individual book

Book component

Now, create a component called Book.svelte and place it in the src/lib/components/books folder. Place the following content to the Book.svelte file.

<script>
  let { bookId } = $props();
  let bookChapters = {
    1: [1, 2, 3],
    2: [1, 2, 3, 4],
    3: [1, 2, 3, 4, 5, 6],
  };

  let chapters = bookChapters[bookId] || [];
</script>

<h1>Book {bookId}</h1>
<ul>
  {#each chapters as chapter}
    <li>
      <a href={`/books/${bookId}/chapters/${chapter}`}>Chapter {chapter}</a>
    </li>
  {/each}
</ul>

The component accepts one property, bookId, which is used to display the book’s ID. The id is also used to decide which chapters to show for the book. For now, the chapters are stored in a simple object, where the key is the book ID, and the value is an array of chapter numbers.

Book page

Next, modify the src/routes/books/[bookId]/+page.svelte file to use the Book component. The page will now look like this:

<script>
  import Book from "$lib/components/books/Book.svelte";

  let { data } = $props();
</script>

<Book bookId={data.bookId} />

Now, when you navigate to the address /books/1, you should see the book with ID 1, and a list of chapters for that book. Similarly, when you navigate to /books/2, you should see the book with ID 2, and so on. This is shown in Figure 2 below.

Fig 2. -- The path /books/1 shows the book with ID 1, and a list of chapters for that book.

Fig 2. — The path /books/1 shows the book with ID 1, and a list of chapters for that book.

Separate chapter list component

We can further expand the example by introducing a separate component for displaying the list of chapters. This component will accept the property bookId. Create a component called ChapterList.svelte to the src/lib/components/books folder, and place the following content to the file.

<script>
  let { bookId } = $props();
  let bookChapters = {
    1: [1, 2, 3],
    2: [1, 2, 3, 4],
    3: [1, 2, 3, 4, 5, 6],
  };

  let chapters = bookChapters[bookId] || [];
</script>

<ul>
  {#each chapters as chapter}
    <li>
      <a href={`/books/${bookId}/chapters/${chapter}`}>Chapter {chapter}</a>
    </li>
  {/each}
</ul>

Then, modify the Book.svelte component to use the ChapterList component. The modified Book.svelte file would look as follows:

<script>
  import ChapterList from "$lib/components/books/ChapterList.svelte";
  let { bookId } = $props();
</script>

<h1>Book {bookId}</h1>

<ChapterList bookId={bookId} />

Now, when you navigate to the address /books/1, you should see the book with ID 1, and a list of chapters for that book, just like before. The only difference is that the chapter list is now rendered by the ChapterList component.

Summary

To summarize:

  • Properties allow passing data from a parent component to a child component.
  • Properties are declared using the let { propertyName } = $props(); syntax.
  • Properties are passed using the syntax <Component propertyName={value} />.
  • Properties from dynamic route parameters can be passed to components.
Loading Exercise...