Skip to main content

Welcome to a Grasp lesson

Create a free account to keep studying and create your own study path.

Lesson 6

Passing Props with `$props()`

Recommended resources

Welcome back! In our last session, we successfully composed a UI by importing and rendering multiple ProfileCard components on a single page. However, you likely noticed the main limitation: every card was identical because the data was hard-coded inside the ProfileCard component itself.

Today, we're going to solve that problem by making your components dynamic and reusable. We will explore one of the most fundamental concepts in component-based architecture: passing data from a parent component to a child. You'll learn how to use Svelte 5's $props() rune to declare the data a component expects, and then pass that data down from your main page. This is Svelte's equivalent to how you pass props in React, but with a clean, modern syntax designed for clarity and performance.

By the end of this lesson, you'll be able to create truly reusable components by populating them with unique data from their parent.

Understanding Props in Svelte 5

Props (short for properties) are the standard mechanism for a parent component to communicate with its children. The parent passes data, and the child receives and uses it. In Svelte 5, this is managed with the $props() rune.

The video below from the "Svelte 5 Basics" course provides an excellent, practical introduction. It demonstrates exactly how to declare a prop in a child component and how to pass a value to it from the parent.

Svelte 5 Basics - Complete Svelte 5 Course for Beginners

Watch this segment to see the $props() rune in action.

Pay close attention to the syntax used in both the child and parent components. Focus on the core concept of declaring props with let {...} = $props() and passing them like HTML attributes. The instructor also explains what runes are in Svelte 5, which is a key concept for the rest of this course.

As you saw, the process involves two key steps:

  1. In the child component (Header.svelte in the video): You declare the props it accepts using let { propName } = $props(); inside the <script> tag.
  2. In the parent component (+page.svelte in the video): You pass the data to the child using an attribute-like syntax: <Header propName="some value" />.

Refactoring Your ProfileCard

Now, let's apply this to your ProfileCard project. Your goal is to stop hard-coding the user's name, bio, and avatar and instead receive that information via props.

1. Modify the Child Component (ProfileCard.svelte)

First, open src/lib/components/ProfileCard.svelte.

In the <script> block, declare the props that this component will receive: name, bio, and avatarUrl. We'll also add TypeScript types for clarity, a practice you'll find helpful in larger projects.

// src/lib/components/ProfileCard.svelte

<script lang="ts">
  let {
    name,
    bio,
    avatarUrl
  }: {
    name: string;
    bio: string;
    avatarUrl: string;
  } = $props();
</script>

// ... rest of the file

Now, go down to your markup and replace the hard-coded text and image source with these new prop variables. Remember to use curly braces {} to render JavaScript expressions and variables in your Svelte markup, much like you do in JSX.

Find this div:

<div class="text-center">
  <h2 class="text-xl font-semibold text-slate-800">Alex Doe</h2>
  <p class="mt-1 text-sm text-slate-600">Svelte & Tailwind Enthusiast</p>
</div>

And change it to use the name and bio props:

<div class="text-center">
  <h2 class="text-xl font-semibold text-slate-800">{name}</h2>
  <p class="mt-1 text-sm text-slate-600">{bio}</p>
</div>

Next, find the img tag:

<img
  class="mx-auto h-24 w-24 rounded-full"
  src="https://images.unsplash.com/photo-1535713875002-d1d0cf377fDE?q=80&w=2080&auto=format&fit=crop"
  alt="Profile"
/>

And change its src attribute to use the avatarUrl prop:

<img
  class="mx-auto h-24 w-24 rounded-full"
  src={avatarUrl}
  alt="Profile of {name}"
/>

Notice we also made the alt text dynamic. Your ProfileCard is now a flexible template waiting for data.

2. Update the Parent Component (+page.svelte)

If you look at your running application now, it's probably broken! That's because the ProfileCard component now requires name, bio, and avatarUrl props, but the parent component (+page.svelte) isn't providing them. Let's fix that.

Open src/routes/+page.svelte.

First, let's define some data for our team members inside the <script> block. This simulates fetching data that you might get from an API.

// src/routes/+page.svelte

<script lang="ts">
  import ProfileCard from '$lib/components/ProfileCard.svelte';

  const alex = {
    name: 'Alex Doe',
    bio: 'Svelte & Tailwind Enthusiast',
    avatarUrl: 'https://images.unsplash.com/photo-1535713875002-d1d0cf377fDE?q=80&w=2080&auto=format&fit=crop'
  };

  const jane = {
    name: 'Jane Smith',
    bio: 'Full-Stack Developer',
    avatarUrl: 'https://images.unsplash.com/photo-1494790108377-be9c29b29330?q=80&w=1887&auto=format&fit=crop'
  };
    
  const sam = {
    name: 'Sam Wilson',
    bio: 'UI/UX Designer',
    avatarUrl: 'https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?q=80&w=1887&auto=format&fit=crop'
  };
</script>

// ... markup follows

Now, update the <ProfileCard /> components in your markup to pass this data down.

// src/routes/+page.svelte

// ... script block from above

<div class="flex min-h-screen flex-wrap items-center justify-center gap-8 bg-slate-100 p-8">
  <ProfileCard
    name={alex.name}
    bio={alex.bio}
    avatarUrl={alex.avatarUrl}
  />
  <ProfileCard
    name={jane.name}
    bio={jane.bio}
    avatarUrl={jane.avatarUrl}
  />
  <ProfileCard
    name={sam.name}
    bio={sam.bio}
    avatarUrl={sam.avatarUrl}
  />
</div>

Save your file and check the browser. You should now see three different profile cards, each displaying unique information. You've successfully decoupled the data from the presentation!

For a concise written summary that also frames this concept from a React developer's perspective, the following article is a great read.

Getting Started with Svelte 5: A Guide for React Developers

This article provides a direct comparison between props in Svelte and React, reinforcing what you've just implemented.

Please read the section titled Props. Notice how the Svelte example is structured very similarly to what you just built.


Prop Enhancements: Shorthand and Defaults

Svelte includes some convenient features to make working with props even easier.

Prop Shorthand
A common pattern is having a variable in the parent with the same name as the prop you want to pass it to (e.g., a variable name passed to a prop name). Svelte has a beautiful shorthand for this.

Instead of writing name={name}, you can simply write {name}. Let's refactor the first profile card to use this. First, we need to destructure our alex object in the script.

// In src/routes/+page.svelte script tag
<script lang="ts">
  import ProfileCard from '$lib/components/ProfileCard.svelte';

  const { name, bio, avatarUrl } = {
    name: 'Alex Doe',
    bio: 'Svelte & Tailwind Enthusiast',
    avatarUrl: 'https://images.unsplash.com/photo-1535713875002-d1d0cf377fDE?q=80&w=2080&auto=format&fit=crop'
  };
  
  // ... jane and sam objects
</script>

Now you can use the shorthand in the markup:

// In src/routes/+page.svelte markup
<ProfileCard {name} {bio} {avatarUrl} />

This is purely syntactic sugar, but it leads to cleaner code, especially when passing many props.

Default Prop Values
What if a prop is optional? You can provide a default value directly in the child component's $props() declaration. Let's make the bio prop optional and give it a default value in ProfileCard.svelte.

// src/lib/components/ProfileCard.svelte

<script lang="ts">
  let {
    name,
    bio = 'A member of our team', // Default value
    avatarUrl
  }: {
    name: string;
    bio?: string; // Mark as optional in TypeScript
    avatarUrl: string;
  } = $props();
</script>

Now, if you remove the bio prop from one of the ProfileCard instances in +page.svelte, it will render "A member of our team" instead of breaking. This makes your components more robust and versatile.


Conclusion

In this lesson, you've mastered a critical pattern in Svelte development: passing data from a parent to a child using props. By leveraging the $props() rune, you transformed your static ProfileCard into a dynamic, reusable component.

Key Takeaways:

  • Props for Data Flow: Props are the primary way to pass data down the component tree, from parent to child.
  • Declaring Props: In Svelte 5, child components declare the props they accept using let { ... } = $props();.
  • Passing Props: Parent components pass data to children using an attribute-like syntax, e.g., <Child propName={value} />.
  • Shorthand Syntax: When a variable name matches the prop name, you can use the {variable} shorthand for more concise code.
  • Default Values: You can make props optional and components more resilient by providing default values in the $props() declaration.

Your ProfileCard is now styled and can accept dynamic data. The next logical step is to ensure it looks great on any device. In our next lesson, we will dive into responsive design, using TailwindCSS's powerful breakpoint prefixes to adapt your component's layout for mobile, tablet, and desktop screens.