1//! Definition for the [`NoSsr`] and [`NoHydrate`] components.
23use sycamore_macro::{component, view, Props};
45use crate::*;
67/// Props for [`Show`].
8#[derive(Props)]
9pub struct ShowProps {
10#[prop(setter(into))]
11pub when: MaybeDyn<bool>,
12pub children: Children,
13}
1415/// An utility component that only renders its children when a condition is satisfied.
16#[component]
17pub fn Show(props: ShowProps) -> View {
18let mut children = props.children.call();
19let when = create_selector(move || props.when.get());
2021if is_ssr!() {
22 View::from_dynamic(move || {
23if when.get() {
24 std::mem::take(&mut children)
25 } else {
26view! {}
27 }
28 })
29 } else {
30 View::from_dynamic(move || {
31let cloned = utils::clone_nodes_via_web_sys(&children);
3233if when.get() {
34// Do not set `children` to the document fragment since the document fragment will
35 // be emptied when it is appended.
36children = utils::unwrap_from_document_fragment(cloned);
3738 utils::clone_nodes_via_web_sys(&children)
39 } else {
40// Wrap children inside a document fragment so that it can still be dynamically
41 // updated even though it is not mounted.
42children = utils::wrap_in_document_fragment(cloned);
43view! {}
44 }
45 })
46 }
47}
4849/// Component that is only renders its children on the client side.
50///
51/// This is useful when wrapping parts of your app that are not intended to be server-side
52/// rendered, e.g. highly interactive components such as graphs, etc...
53#[component(inline_props)]
54pub fn NoSsr(children: Children) -> View {
55if is_ssr!() {
56view! { no-ssr() }
57 } else {
58let marker = create_node_ref();
59let view = view! { no-ssr(r#ref=marker) };
60 on_mount(move || {
61let marker = marker.get();
62let parent = marker.parent_node().unwrap();
6364let children = children.call();
65for node in children.as_web_sys() {
66 parent.insert_before(&node, Some(&marker)).unwrap();
67 }
68 parent.remove_child(&marker).unwrap();
69 });
70 view
71 }
72}
7374/// Components that do not need, or should not be hydrated on the client side.
75///
76/// This is useful when large parts of your app do not require client-side interactivity such as
77/// static content.
78///
79/// However, this component will still be rendered on the client side if it is created after the
80/// initial hydration phase is over, e.g. navigating to a new page with a `NoHydrate` component.
81#[component(inline_props)]
82pub fn NoHydrate(children: Children) -> View {
83if is_ssr!() {
84let is_hydrating = IS_HYDRATING.replace(false);
85let children = children.call();
86 IS_HYDRATING.set(is_hydrating);
87 children
88 } else if IS_HYDRATING.get() {
89view! {}
90 } else {
91 children.call()
92 }
93}
9495/// Generate a script element for bootstrapping hydration.
96///
97/// In general, prefer using [`HydrationScript`] instead.
98pub fn generate_hydration_script(mode: SsrMode) -> &'static str {
99match mode {
100 SsrMode::Sync => "",
101 SsrMode::Blocking => "window.__sycamore_ssr_mode='blocking'",
102 SsrMode::Streaming => "window.__sycamore_ssr_mode='streaming'",
103 }
104}
105106/// Component that creates a script element for bootstrapping hydration. Should be rendered into
107/// the `<head>` of the document.
108///
109/// This component is required if using SSR in blocking or streaming mode.
110///
111/// TODO: use this component to also capture and replay events. This requires synthetic event
112/// delegation: <https://github.com/sycamore-rs/sycamore/issues/176>
113#[component]
114pub fn HydrationScript() -> View {
115is_ssr! {
116let mode = use_context::<SsrMode>();
117let script = generate_hydration_script(mode);
118view! {
119 NoHydrate {
120 script(dangerously_set_inner_html=script)
121 }
122 }
123 }
124is_not_ssr! {
125view! {}
126 }
127}