Data Flows Down
Components receive data from their parents through props. In Svelte 5, the $props() rune handles this. Understanding props is essential — it’s how you build configurable, reusable components.
What You’ll Learn
- Declare props with
$props() - Pass data to components
- Set default values
- Destructure multiple props
The $props Rune
Props are declared by destructuring the result of $props():
<!-- filename: src/lib/components/ServiceCard.svelte -->
<script>
let { service } = $props();
</script>
<article class="service-card">
<h3>{service.name}</h3>
<p>{service.description}</p>
<p>${service.price}</p>
</article> The component expects a service prop. When used:
<ServiceCard service={lawnMowing} /> The lawnMowing object becomes available as service inside the component.
Passing Props
There are several ways to pass props:
Named prop:
<ServiceCard service={myService} /> Shorthand (when variable name matches prop name):
<script>
const service = { name: 'Lawn Mowing', price: 50 };
</script>
<!-- Shorthand: {service} equals service={service} -->
<ServiceCard {service} /> Literal value:
<ServiceCard service={{ name: 'Test', price: 0 }} /> Expression:
<ServiceCard service={services.find(s => s.slug === 'lawn-mowing')} /> Multiple Props
Components can accept multiple props:
<!-- filename: src/lib/components/ServiceCard.svelte -->
<script>
let { service, selected, showPrice } = $props();
</script>
<article class="service-card" class:selected>
<h3>{service.name}</h3>
<p>{service.description}</p>
{#if showPrice}
<p class="price">${service.price}</p>
{/if}
</article> Pass them when using the component:
<ServiceCard
service={lawnMowing}
selected={true}
showPrice={true}
/>
<!-- Or with shorthand where applicable -->
<ServiceCard {service} {selected} showPrice /> Boolean props can be passed without a value — showPrice equals showPrice={true}.
Default Values
Set defaults for optional props:
<script>
let {
service,
selected = false,
showPrice = true,
showDuration = true
} = $props();
</script> Now these work:
<!-- Uses defaults: selected=false, showPrice=true, showDuration=true -->
<ServiceCard {service} />
<!-- Override specific defaults -->
<ServiceCard {service} selected={true} showDuration={false} /> Required props (no default) will cause a warning if not provided.
Props Are Read-Only
Props flow down from parent to child. You cannot reassign them:
<script>
let { service } = $props();
// ❌ This will cause an error
service = { name: 'New Service' };
</script> If you need to modify prop data, create local state:
<script>
let { service } = $props();
// ✅ Create local state from prop
let localService = $state({ ...service });
function updateName(newName) {
localService.name = newName;
}
</script> Reactive Props
Props are automatically reactive. When the parent updates the data, the child re-renders:
<!-- Parent -->
<script>
let currentService = $state(services[0]);
function selectNext() {
const index = services.indexOf(currentService);
currentService = services[(index + 1) % services.length];
}
</script>
<ServiceCard service={currentService} />
<button onclick={selectNext}>Next Service</button> When currentService changes, ServiceCard automatically updates.
Accessing All Props
Sometimes you need all props as an object:
<script>
let props = $props();
// Access individual props
console.log(props.service);
console.log(props.selected);
</script> Or combine destructuring with rest:
<script>
let { service, ...rest } = $props();
// service is extracted
// rest contains all other props
</script> This is useful for forwarding props to other elements.
Update ServiceCard with Props
Let’s enhance the ServiceCard with proper props:
<!-- filename: src/lib/components/ServiceCard.svelte -->
<script>
let {
service,
selected = false,
showDescription = true,
showPrice = true,
showDuration = true
} = $props();
</script>
<article class="service-card" class:selected>
<h3>
<a href="/services/{service.slug}">{service.name}</a>
</h3>
{#if showDescription && service.description}
<p class="description">{service.description}</p>
{/if}
<div class="meta">
{#if showPrice}
<span class="price">${service.price}</span>
{/if}
{#if showDuration}
<span class="duration">{service.duration} min</span>
{/if}
</div>
</article>
<style>
.service-card {
padding: 1rem;
border: 2px solid #ddd;
border-radius: 8px;
background: white;
transition: all 0.2s;
}
.service-card:hover {
border-color: #007bff;
}
.service-card.selected {
border-color: #007bff;
background: #f0f7ff;
}
.service-card h3 {
margin: 0 0 0.5rem 0;
}
.service-card h3 a {
color: inherit;
text-decoration: none;
}
.description {
color: #666;
margin: 0 0 0.75rem 0;
}
.meta {
display: flex;
gap: 1rem;
}
.price {
font-weight: bold;
color: #007bff;
}
.duration {
color: #666;
font-size: 0.875rem;
}
</style> Now use it with various configurations:
<!-- Full display -->
<ServiceCard {service} />
<!-- Selected state -->
<ServiceCard {service} selected={true} />
<!-- Compact (no description) -->
<ServiceCard {service} showDescription={false} />
<!-- Minimal -->
<ServiceCard {service} showDescription={false} showDuration={false} /> Common Mistakes
Forgetting to Destructure
<script>
// ❌ Props aren't accessible
$props();
// ✅ Destructure to access
let { service } = $props();
</script> Trying to Mutate Props
<script>
let { items } = $props();
// ❌ Can't push to a prop
items.push(newItem);
// ✅ Tell parent to update, or use local state
let localItems = $state([...items]);
localItems.push(newItem);
</script> Missing Required Props
<!-- ❌ service is required but not provided -->
<ServiceCard />
<!-- ✅ Provide required props -->
<ServiceCard {service} /> Summary
Props are how data flows into components. Use $props() to declare them, destructure to access individual props, and set defaults for optional ones. Props are reactive and read-only.
Key takeaways:
- Declare with
let { prop } = $props() - Set defaults:
let { prop = defaultValue } = $props() - Props are read-only — don’t reassign them
- Props automatically trigger re-renders when changed
Next Steps
ServiceCard is looking good. Continue with Create BookingForm Component to extract the form logic into its own component.