You are reading release documentation. Read the latest development docs.
Migration Guide: v0.9 to v0.10
Warning: v0.10 is still currently in active development. Any content below is a work in progress and not finalised yet.
Components and component props
This release contains several changes to how components and component props are used and defined.
Implicit props by default
Every component now uses inline props by default. Support for struct props has been removed. To migrate, either move all fields from your props struct to function arguments, or pass the entire struct as a single prop instead.
// Old
#[derive(Props)]
struct FooProps {
bar: i32
}
#[component]
fn Foo(props: FooProps) - View { ... }
// Old, with inline props
#[component(inline_props)]
fn Foo(bar: i32) - View { ... }
// New
#[component]
fn Foo(bar: i32) - View { ... }
New #[prop] attributes
The behavior of #[prop] attributes on component props has changed. The
previous behavior was not well documented at all and was an ad-hoc modification
of the typed-builder crate.
The new behavior is intentionally kept simple and adapted to Sycamore’s use
case. For more details, see the doc page on the
#[component]
proc-macro.
Here is a quick example of the new syntax
#[component]
fn Foo(
// Set the default value of the prop if no explicit value is passed.
#[prop(default = 42)] x: i32,
// Accept anything that can be converted to a String.
#[prop(into)] y: String,
// Option types are optional by default.
z: Optioni32
// This behavior can also be disabled.
#[prop(required)]
w: Optioni32
) - View { ... }
Components automatically return builders
The #[component] function now transforms component functions to return a
fluent-style builder instead of just the raw component function. For users of
the view! macro, this shouldn’t change anything but for builder API users,
this makes components much nicer to use.
// Old
#[component(implicit_props)]
fn Foo(bar: i32) - View { ... }
div().children(
Foo(FooProps::builder().bar(123).build())
)
// New
#[component]
fn Foo(bar: i32) - View { ... }
div().children(
Foo().bar(i32)
)
This unifies the syntax between components and built-in elements.
New syntax for attribute passthrough
The way attribute passthrough is defined for a component has changed to be more flexible for alternative rendering backends.
// Old
#[component(implicit_props)]
fn MyButton(#[prop(attributes(html, button))] attributes: Attributes) - View {
...
}
// New
#[component(extend_element!(html:button))]
fn MyButton(attributes: Attributes) - View {
...
}
Interpolated text strings
String literals used as text nodes in view! now support reactive interpolation:
view! {
p { "Hello, {name}!" }
}
Literal braces in text nodes must now be escaped as {{ and }}, just like in
format!. For example, change "{}" to "{{}}". This also applies to raw string
literals, including inline CSS in style elements. String literals used as
attributes or component props are unchanged.
Explicit reactive attributes and properties
The view! macro no longer automatically wraps element attribute or DOM
property expressions in closures. Like component props, expressions are now
passed directly. Use an explicit closure when an expression should update
reactively:
// Old
view! {
button(disabled=is_editing.get())
progress(prop:value=progress.get()[0])
}
// New
view! {
button(disabled=move || is_editing.get())
progress(prop:value=move || progress.get()[0])
}
This also applies to hyphenated and quoted attributes and prop: properties
passed through components, for example
MyInput(data-count=move || count.get().to_string()).
Without a closure, a signal read such as disabled=is_editing.get() is evaluated
once and will not update, even though it still compiles. Passing a signal directly
where supported (for example disabled=is_editing) continues to work.
Child expressions and the DSL’s if/match expressions still automatically
track reactive dependencies. Event handlers, bindings, refs, and attribute
spreads continue to be passed directly.
Event handlers are now normal methods
Instead of the bespoke on:event=... syntax for event handlers, all events
should be attached using on_event=... syntax. For example:
// Old
view! {
div(on:click=|| { ... })
}
// New
view! {
div(on_click=|| { ... })
}
This change also makes the builder API slightly simpler to use.
// Old
div().on(event::click, || { ... })
// New
div().on_click(|| { ... })