Skip to content

Commit 43fdf11

Browse files
committed
feat: made #[template_rx] support unreactive state
This involved creating a derive macro for `UnreactiveState`, allowing such state to work seamlessly with Perseus' usual reactive state platform. With this and the recent global state improvements, users of unreactive state can now use reactive global state, preloading, etc. BREAKING CHANGE: renamed `#[template_rx]` to `#[template]` (unreactive templates should now use `#[template(unreactive)]`)
1 parent 9a9a1be commit 43fdf11

File tree

53 files changed

+301
-324
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

53 files changed

+301
-324
lines changed

docs/0.4.x/en-US/tutorials/second-app.md

+3-3
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ The first thing then is `IndexPageState`, which is our first usage of Perseus' s
6060

6161
Importantly, we've annotated that with `#[perseus::make_rx(IndexPageStateRx)]`, which will create a version of this `struct` that uses Sycamore's `Signal`s: a reactive version. If you're unfamiliar with Sycamore's reactivity system, you should read [this](https://sycamore-rs.netlify.app/docs/basics/reactivity) quickly before continuing.
6262

63-
Next, we create a function called `index_page`, which we annotate with `#[perseus::template_rx]`. That macro is used for declaring templates, and you can think of it like black box that makes things work.
63+
Next, we create a function called `index_page`, which we annotate with `#[perseus::template]`. That macro is used for declaring templates, and you can think of it like black box that makes things work.
6464

6565
<details>
6666
<summary>Details??</summary>
@@ -94,7 +94,7 @@ Notably, we could actually change this value at runtime if we wanted by calling
9494

9595
The next function we define is `get_template()`, which is fairly straightforward. It just declares a [`Template`](=struct.Template@perseus) with the necessary properties. Specifically, we define the function that actually renders the template as `index_page`, and the other two we'll get to now.
9696

97-
The first of those is the `head()` function, which is annotated as `#[perseus::head]` (which has similar responsibilities to `#[perseus::template_rx]`). In HTML, the language for creating views on the web, there are two main components to every page: the `<body>` and the `<head>`, the latter of which defines certain metadata, like the title, and any stylesheets you need, for example. If `index_page()` creates the body, then `head()` creates the head in this example. Notably, because the head is rendered only ahead of time, it can't be reactive. For that reason, it's passed the unreactive version of the state, and, rather than being generic over `Html`, it uses [`SsrNode`](=struct.SsrNode@perseus), which is specialized for the engine-side.
97+
The first of those is the `head()` function, which is annotated as `#[perseus::head]` (which has similar responsibilities to `#[perseus::template]`). In HTML, the language for creating views on the web, there are two main components to every page: the `<body>` and the `<head>`, the latter of which defines certain metadata, like the title, and any stylesheets you need, for example. If `index_page()` creates the body, then `head()` creates the head in this example. Notably, because the head is rendered only ahead of time, it can't be reactive. For that reason, it's passed the unreactive version of the state, and, rather than being generic over `Html`, it uses [`SsrNode`](=struct.SsrNode@perseus), which is specialized for the engine-side.
9898

9999
Because this function will only ever run on the engine-side, `#[perseus::head]` implies a target-gate to the engine (i.e. `#[cfg(not(target_arch = "wasm32"))]` is implicit). This means you can use engine-side dependencies here without any extra gating.
100100

@@ -123,7 +123,7 @@ With that done, we can build the second template of this app, which is much simp
123123

124124
This is basically a simpler version of the index template, with no state, and this template only defines a simple view and some metadata in the head.
125125

126-
Importantly, this illustrates that templates that don't take state don't have to have a second argument for their nonexistent state, the `#[perseus::template_rx]` macro is smart enough to handle that (and even a third argument for global state).
126+
Importantly, this illustrates that templates that don't take state don't have to have a second argument for their nonexistent state, the `#[perseus::template]` macro is smart enough to handle that (and even a third argument for global state).
127127

128128
## Error pages
129129

docs/next/en-US/tutorials/second-app.md

+3-3
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ The first thing then is `IndexPageState`, which is our first usage of Perseus' s
6060

6161
Importantly, we've annotated that with `#[perseus::make_rx(IndexPageStateRx)]`, which will create a version of this `struct` that uses Sycamore's `Signal`s: a reactive version. If you're unfamiliar with Sycamore's reactivity system, you should read [this](https://sycamore-rs.netlify.app/docs/basics/reactivity) quickly before continuing.
6262

63-
Next, we create a function called `index_page`, which we annotate with `#[perseus::template_rx]`. That macro is used for declaring templates, and you can think of it like black box that makes things work.
63+
Next, we create a function called `index_page`, which we annotate with `#[perseus::template]`. That macro is used for declaring templates, and you can think of it like black box that makes things work.
6464

6565
<details>
6666
<summary>Details??</summary>
@@ -94,7 +94,7 @@ Notably, we could actually change this value at runtime if we wanted by calling
9494

9595
The next function we define is `get_template()`, which is fairly straightforward. It just declares a [`Template`](=struct.Template@perseus) with the necessary properties. Specifically, we define the function that actually renders the template as `index_page`, and the other two we'll get to now.
9696

97-
The first of those is the `head()` function, which is annotated as `#[perseus::head]` (which has similar responsibilities to `#[perseus::template_rx]`). In HTML, the language for creating views on the web, there are two main components to every page: the `<body>` and the `<head>`, the latter of which defines certain metadata, like the title, and any stylesheets you need, for example. If `index_page()` creates the body, then `head()` creates the head in this example. Notably, because the head is rendered only ahead of time, it can't be reactive. For that reason, it's passed the unreactive version of the state, and, rather than being generic over `Html`, it uses [`SsrNode`](=struct.SsrNode@perseus), which is specialized for the engine-side.
97+
The first of those is the `head()` function, which is annotated as `#[perseus::head]` (which has similar responsibilities to `#[perseus::template]`). In HTML, the language for creating views on the web, there are two main components to every page: the `<body>` and the `<head>`, the latter of which defines certain metadata, like the title, and any stylesheets you need, for example. If `index_page()` creates the body, then `head()` creates the head in this example. Notably, because the head is rendered only ahead of time, it can't be reactive. For that reason, it's passed the unreactive version of the state, and, rather than being generic over `Html`, it uses [`SsrNode`](=struct.SsrNode@perseus), which is specialized for the engine-side.
9898

9999
Because this function will only ever run on the engine-side, `#[perseus::head]` implies a target-gate to the engine (i.e. `#[cfg(not(target_arch = "wasm32"))]` is implicit). This means you can use engine-side dependencies here without any extra gating.
100100

@@ -123,7 +123,7 @@ With that done, we can build the second template of this app, which is much simp
123123

124124
This is basically a simpler version of the index template, with no state, and this template only defines a simple view and some metadata in the head.
125125

126-
Importantly, this illustrates that templates that don't take state don't have to have a second argument for their nonexistent state, the `#[perseus::template_rx]` macro is smart enough to handle that (and even a third argument for global state).
126+
Importantly, this illustrates that templates that don't take state don't have to have a second argument for their nonexistent state, the `#[perseus::template]` macro is smart enough to handle that (and even a third argument for global state).
127127

128128
## Error pages
129129

examples/core/basic/src/templates/about.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use perseus::Template;
22
use sycamore::prelude::{view, Html, Scope, SsrNode, View};
33

4-
#[perseus::template_rx]
4+
#[perseus::template]
55
pub fn about_page<G: Html>(cx: Scope) -> View<G> {
66
view! { cx,
77
p { "About." }

examples/core/basic/src/templates/index.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ pub struct IndexPageState {
66
pub greeting: String,
77
}
88

9-
#[perseus::template_rx]
9+
#[perseus::template]
1010
pub fn index_page<'a, G: Html>(cx: Scope<'a>, state: IndexPageStateRx<'a>) -> View<G> {
1111
view! { cx,
1212
p { (state.greeting.get()) }

examples/core/custom_server/src/templates/about.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use perseus::Template;
22
use sycamore::prelude::{view, Html, Scope, View};
33

4-
#[perseus::template_rx]
4+
#[perseus::template]
55
pub fn about_page<G: Html>(cx: Scope) -> View<G> {
66
view! { cx,
77
p { "About." }

examples/core/custom_server/src/templates/index.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use perseus::{Html, Template};
22
use sycamore::prelude::{view, Scope, View};
33

4-
#[perseus::template_rx]
4+
#[perseus::template]
55
pub fn index_page<'a, G: Html>(cx: Scope<'a>) -> View<G> {
66
view! { cx,
77
p { "Hello World!" }

examples/core/freezing_and_thawing/src/templates/about.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use sycamore::prelude::*;
44

55
use crate::global_state::AppStateRx;
66

7-
#[perseus::template_rx]
7+
#[perseus::template]
88
pub fn about_page<'a, G: Html>(cx: Scope<'a>) -> View<G> {
99
// This is not part of our data model, we do NOT want the frozen app
1010
// synchronized as part of our page's state, it should be separate

examples/core/freezing_and_thawing/src/templates/index.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ pub struct IndexProps {
99
username: String,
1010
}
1111

12-
#[perseus::template_rx]
12+
#[perseus::template]
1313
pub fn index_page<'a, G: Html>(cx: Scope<'a>, state: IndexPropsRx<'a>) -> View<G> {
1414
// This is not part of our data model, we do NOT want the frozen app
1515
// synchronized as part of our page's state, it should be separate

examples/core/global_state/src/templates/about.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use sycamore::prelude::*;
33

44
use crate::global_state::AppStateRx;
55

6-
#[perseus::template_rx]
6+
#[perseus::template]
77
pub fn about_page<'a, G: Html>(cx: Scope<'a>) -> View<G> {
88
let global_state = RenderCtx::from_ctx(cx).get_global_state::<AppStateRx>(cx);
99

examples/core/global_state/src/templates/index.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use crate::global_state::AppStateRx;
55

66
// Note that this template takes no state of its own in this example, but it
77
// certainly could
8-
#[perseus::template_rx]
8+
#[perseus::template]
99
pub fn index_page<'a, G: Html>(cx: Scope<'a>) -> View<G> {
1010
// We access the global state through the render context, extracted from
1111
// Sycamore's context system

examples/core/i18n/src/templates/about.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use perseus::{t, Template};
22
use sycamore::prelude::{view, Html, Scope, View};
33

4-
#[perseus::template_rx]
4+
#[perseus::template]
55
pub fn about_page<G: Html>(cx: Scope) -> View<G> {
66
view! { cx,
77
p { (t!("about", cx)) }

examples/core/i18n/src/templates/index.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use perseus::{link, t, Template};
22
use sycamore::prelude::{view, Html, Scope, View};
33

4-
#[perseus::template_rx]
4+
#[perseus::template]
55
pub fn index_page<G: Html>(cx: Scope) -> View<G> {
66
let username = "User";
77

examples/core/i18n/src/templates/post.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ pub struct PostPageState {
77
content: String,
88
}
99

10-
#[perseus::template_rx]
10+
#[perseus::template]
1111
pub fn post_page<'a, G: Html>(cx: Scope<'a>, props: PostPageStateRx<'a>) -> View<G> {
1212
let title = props.title;
1313
let content = props.content;

examples/core/idb_freezing/src/templates/about.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use sycamore::prelude::*;
33

44
use crate::global_state::AppStateRx;
55

6-
#[perseus::template_rx]
6+
#[perseus::template]
77
pub fn about_page<'a, G: Html>(cx: Scope<'a>) -> View<G> {
88
// This is not part of our data model
99
let freeze_status = create_signal(cx, String::new());

examples/core/idb_freezing/src/templates/index.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ pub struct IndexProps {
88
username: String,
99
}
1010

11-
#[perseus::template_rx]
11+
#[perseus::template]
1212
pub fn index_page<'a, G: Html>(cx: Scope<'a>, state: IndexPropsRx<'a>) -> View<G> {
1313
// This is not part of our data model
1414
let freeze_status = create_signal(cx, String::new());

examples/core/index_view/src/templates/about.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use perseus::Template;
22
use sycamore::prelude::{view, Html, Scope, View};
33

4-
#[perseus::template_rx]
4+
#[perseus::template]
55
pub fn about_page<G: Html>(cx: Scope) -> View<G> {
66
view! { cx,
77
p { "About." }

examples/core/index_view/src/templates/index.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use perseus::{Html, Template};
22
use sycamore::prelude::{view, Scope, View};
33

4-
#[perseus::template_rx]
4+
#[perseus::template]
55
pub fn index_page<G: Html>(cx: Scope) -> View<G> {
66
view! { cx,
77
p { "Hello World!" }

examples/core/js_interop/src/templates/index.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use sycamore::prelude::{view, Scope, View};
33
#[cfg(target_arch = "wasm32")]
44
use wasm_bindgen::prelude::wasm_bindgen;
55

6-
#[perseus::template_rx]
6+
#[perseus::template]
77
pub fn index_page<'a, G: Html>(cx: Scope<'a>) -> View<G> {
88
view! { cx,
99
// We'll use JS to change this message manually

examples/core/plugins/src/templates/about.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use perseus::Template;
22
use sycamore::prelude::{view, Html, Scope, SsrNode, View};
33

44
// This page will actually be replaced entirely by a plugin!
5-
#[perseus::template_rx]
5+
#[perseus::template]
66
pub fn about_page<G: Html>(cx: Scope) -> View<G> {
77
view! { cx,
88
p { "About." }

examples/core/plugins/src/templates/index.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use perseus::{Html, Template};
22
use sycamore::prelude::{view, Scope, SsrNode, View};
33

4-
#[perseus::template_rx]
4+
#[perseus::template]
55
pub fn index_page<G: Html>(cx: Scope) -> View<G> {
66
view! { cx,
77
p { "Hello World!" }

examples/core/preload/src/templates/about.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use perseus::{Html, Template};
22
use sycamore::prelude::{view, Scope};
33
use sycamore::view::View;
44

5-
#[perseus::template_rx]
5+
#[perseus::template]
66
pub fn about_page<G: Html>(cx: Scope) -> View<G> {
77
view! { cx,
88
p { "Check out your browser's network DevTools, no new requests were needed to get to this page!" }

examples/core/preload/src/templates/index.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use perseus::Template;
22
use sycamore::prelude::{view, Html, Scope, SsrNode, View};
33

4-
#[perseus::template_rx]
4+
#[perseus::template]
55
pub fn index_page<G: Html>(cx: Scope) -> View<G> {
66
// We can't preload pages on the engine-side
77
#[cfg(target_arch = "wasm32")]

examples/core/router_state/src/templates/about.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use perseus::{Html, Template};
22
use sycamore::prelude::{view, Scope, SsrNode, View};
33

4-
#[perseus::template_rx]
4+
#[perseus::template]
55
pub fn about_page<G: Html>(cx: Scope) -> View<G> {
66
view! { cx,
77
p { "Hello World!" }

examples/core/router_state/src/templates/index.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use perseus::prelude::*;
22
use perseus::router::RouterLoadState;
33
use sycamore::prelude::*;
44

5-
#[perseus::template_rx]
5+
#[perseus::template]
66
pub fn router_state_page<G: Html>(cx: Scope) -> View<G> {
77
let load_state = RenderCtx::from_ctx(cx).router.get_load_state(cx);
88
// This uses Sycamore's `create_memo` to create a state that will update

examples/core/rx_state/src/templates/about.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use perseus::{Html, Template};
22
use sycamore::prelude::{view, Scope};
33
use sycamore::view::View;
44

5-
#[perseus::template_rx]
5+
#[perseus::template]
66
pub fn about_page<G: Html>(cx: Scope) -> View<G> {
77
view! { cx,
88
p { "Try going back to the index page, and the state should still be the same!" }

examples/core/rx_state/src/templates/index.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ pub struct IndexPageState {
99
// This macro will make our state reactive *and* store it in the page state
1010
// store, which means it'll be the same even if we go to the about page and come
1111
// back (as long as we're in the same session)
12-
#[perseus::template_rx]
12+
#[perseus::template]
1313
pub fn index_page<'a, G: Html>(cx: Scope<'a>, state: IndexPageStateRx<'a>) -> View<G> {
1414
view! { cx,
1515
p { (format!("Greetings, {}!", state.username.get())) }

examples/core/set_headers/src/templates/index.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ struct PageState {
66
greeting: String,
77
}
88

9-
#[perseus::template_rx]
9+
#[perseus::template]
1010
pub fn index_page<'a, G: Html>(cx: Scope<'a>, state: PageStateRx<'a>) -> View<G> {
1111
view! { cx,
1212
p { (state.greeting.get()) }

examples/core/state_generation/src/templates/amalgamation.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ pub struct PageState {
88
pub message: String,
99
}
1010

11-
#[perseus::template_rx]
11+
#[perseus::template]
1212
pub fn amalgamation_page<'a, G: Html>(cx: Scope<'a>, state: PageStateRx<'a>) -> View<G> {
1313
view! { cx,
1414
p { (format!("The message is: '{}'", state.message.get())) }

examples/core/state_generation/src/templates/build_paths.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ pub struct PageState {
77
content: String,
88
}
99

10-
#[perseus::template_rx]
10+
#[perseus::template]
1111
pub fn build_paths_page<'a, G: Html>(cx: Scope<'a>, state: PageStateRx<'a>) -> View<G> {
1212
let title = state.title;
1313
let content = state.content;

examples/core/state_generation/src/templates/build_state.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ pub struct PageState {
66
pub greeting: String,
77
}
88

9-
#[perseus::template_rx]
9+
#[perseus::template]
1010
pub fn build_state_page<'a, G: Html>(cx: Scope<'a>, state: PageStateRx<'a>) -> View<G> {
1111
view! { cx,
1212
p { (state.greeting.get()) }

examples/core/state_generation/src/templates/incremental_generation.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ pub struct PageState {
1010
content: String,
1111
}
1212

13-
#[perseus::template_rx]
13+
#[perseus::template]
1414
pub fn incremental_generation_page<'a, G: Html>(cx: Scope<'a>, state: PageStateRx<'a>) -> View<G> {
1515
let title = state.title;
1616
let content = state.content;

examples/core/state_generation/src/templates/request_state.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ pub struct PageState {
88
ip: String,
99
}
1010

11-
#[perseus::template_rx]
11+
#[perseus::template]
1212
pub fn request_state_page<'a, G: Html>(cx: Scope<'a>, state: PageStateRx<'a>) -> View<G> {
1313
view! { cx,
1414
p {

examples/core/state_generation/src/templates/revalidation.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ pub struct PageState {
66
pub time: String,
77
}
88

9-
#[perseus::template_rx]
9+
#[perseus::template]
1010
pub fn revalidation_page<'a, G: Html>(cx: Scope<'a>, state: PageStateRx<'a>) -> View<G> {
1111
view! { cx,
1212
p { (format!("The time when this page was last rendered was '{}'.", state.time.get())) }

examples/core/state_generation/src/templates/revalidation_and_incremental_generation.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ pub struct PageState {
1010
pub time: String,
1111
}
1212

13-
#[perseus::template_rx]
13+
#[perseus::template]
1414
pub fn revalidation_and_incremental_generation_page<'a, G: Html>(
1515
cx: Scope<'a>,
1616
state: PageStateRx<'a>,

examples/core/static_content/src/templates/index.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use perseus::{Html, Template};
22
use sycamore::prelude::{view, Scope, SsrNode, View};
33

4-
#[perseus::template_rx]
4+
#[perseus::template]
55
pub fn index_page<G: Html>(cx: Scope) -> View<G> {
66
view! { cx,
77
p { "Hello World!" }

examples/core/unreactive/src/templates/about.rs

+3-8
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,7 @@
1-
use perseus::{Html, SsrNode, Template};
2-
use sycamore::prelude::{view, Scope, View};
1+
use perseus::prelude::*;
2+
use sycamore::prelude::*;
33

4-
// With the old template macro, we have to add the Sycamore `#[component(...)]`
5-
// annotation manually and we get unreactive state passed in Additionally,
6-
// global state is not supported at all So there's no way of persisting state
7-
// between templates
8-
#[perseus::template]
9-
#[sycamore::component]
4+
#[perseus::template(unreactive)]
105
pub fn about_page<G: Html>(cx: Scope) -> View<G> {
116
view! { cx,
127
p { "About." }

examples/core/unreactive/src/templates/index.rs

+12-9
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,23 @@
1-
use perseus::{Html, RenderFnResultWithCause, SsrNode, Template};
1+
use perseus::prelude::*;
22
use serde::{Deserialize, Serialize};
3-
use sycamore::prelude::{view, Scope, View};
3+
use sycamore::prelude::*;
44

55
// Without `#[make_rx(...)]`, we have to manually derive `Serialize` and
66
// `Deserialize`
7-
#[derive(Serialize, Deserialize)]
7+
// We derive `UnreactiveState` too, which actually creates a pseudo-reactive
8+
// wrapper for this unreactive type, allowing it to work with Perseus;
9+
// rather strict state platform (this is just a marker trait though)
10+
#[derive(Serialize, Deserialize, Clone, UnreactiveState)]
811
pub struct IndexPageState {
912
pub greeting: String,
1013
}
1114

12-
// With the old template macro, we have to add the Sycamore `#[component(...)]`
13-
// annotation manually and we get unreactive state passed in Additionally,
14-
// global state is not supported at all So there's no way of persisting state
15-
// between templates
16-
#[perseus::template]
17-
#[sycamore::component]
15+
// By adding `unreactive` in brackets, we tell Perseus to expect something with
16+
// `UnreactiveState` derived.
17+
// Otherwise, you can do everything in this macro that you can do with a
18+
// reactive template! Caching, preloading, reactive global state, etc. are all
19+
// supported.
20+
#[perseus::template(unreactive)]
1821
pub fn index_page<G: Html>(cx: Scope, state: IndexPageState) -> View<G> {
1922
view! { cx,
2023
p { (state.greeting) }

0 commit comments

Comments
 (0)