dioxus_server/
document.rs1use std::cell::RefCell;
6
7use dioxus_core::Element;
8use dioxus_core_macro::rsx;
9use dioxus_document::{
10 Document, Eval, LinkProps, MetaProps, NoOpDocument, ScriptProps, StyleProps,
11};
12use dioxus_html::elements::prelude::*;
13use dioxus_html::*;
14use dioxus_ssr::Renderer;
15use parking_lot::RwLock;
16use std::sync::LazyLock;
17
18static RENDERER: LazyLock<RwLock<Renderer>> = LazyLock::new(|| RwLock::new(Renderer::new()));
19
20pub(crate) fn reset_renderer() {
22 RENDERER.write().clear();
23}
24
25#[derive(Default)]
26struct ServerDocumentInner {
27 streaming: bool,
28 title: Option<String>,
29 meta: Vec<Element>,
30 link: Vec<Element>,
31 script: Vec<Element>,
32}
33
34#[derive(Default)]
36pub struct ServerDocument(RefCell<ServerDocumentInner>);
37
38impl ServerDocument {
39 pub(crate) fn title(&self) -> Option<String> {
40 let myself = self.0.borrow();
41 myself.title.as_ref().map(|title| {
42 RENDERER
43 .write()
44 .render_element(rsx! { title { "{title}" } })
45 })
46 }
47
48 pub(crate) fn render(&self, to: &mut impl std::fmt::Write) -> std::fmt::Result {
49 let myself = self.0.borrow();
50 let element = rsx! {
51 {myself.meta.iter().map(|m| rsx! { {m} })}
52 {myself.link.iter().map(|l| rsx! { {l} })}
53 {myself.script.iter().map(|s| rsx! { {s} })}
54 };
55
56 RENDERER.write().render_element_to(to, element)?;
57
58 Ok(())
59 }
60
61 pub(crate) fn start_streaming(&self) {
62 self.0.borrow_mut().streaming = true;
63 }
64
65 pub(crate) fn warn_if_streaming(&self) {
66 if self.0.borrow().streaming {
67 tracing::warn!(
68 "Attempted to insert content into the head after the initial streaming frame. Inserting content into the head only works during the initial render of SSR outside before resolving any suspense boundaries."
69 );
70 }
71 }
72
73 #[track_caller]
76 pub(crate) fn serialize_for_hydration(&self) {
77 #[cfg(feature = "document")]
79 {
80 dioxus_fullstack_core::head_element_hydration_entry()
81 .insert(&!self.0.borrow().streaming, std::panic::Location::caller());
82 }
83 }
84}
85
86impl Document for ServerDocument {
87 fn eval(&self, js: String) -> Eval {
88 NoOpDocument.eval(js)
89 }
90
91 fn set_title(&self, title: String) {
92 self.warn_if_streaming();
93 self.0.borrow_mut().title = Some(title);
94 }
95
96 fn create_meta(&self, props: MetaProps) {
97 self.0.borrow_mut().meta.push(rsx! {
98 meta {
99 name: props.name,
100 charset: props.charset,
101 http_equiv: props.http_equiv,
102 content: props.content,
103 property: props.property,
104 "data": props.data,
105 ..props.additional_attributes,
106 }
107 });
108 }
109
110 fn create_script(&self, props: ScriptProps) {
111 let children = props.script_contents().ok();
112 self.0.borrow_mut().script.push(rsx! {
113 script {
114 src: props.src,
115 defer: props.defer,
116 crossorigin: props.crossorigin,
117 fetchpriority: props.fetchpriority,
118 integrity: props.integrity,
119 nomodule: props.nomodule,
120 nonce: props.nonce,
121 referrerpolicy: props.referrerpolicy,
122 r#type: props.r#type,
123 ..props.additional_attributes,
124 {children}
125 }
126 });
127 }
128
129 fn create_style(&self, props: StyleProps) {
130 let contents = props.style_contents().ok();
131 self.0.borrow_mut().script.push(rsx! {
132 style {
133 media: props.media,
134 nonce: props.nonce,
135 title: props.title,
136 ..props.additional_attributes,
137 {contents}
138 }
139 })
140 }
141
142 fn create_link(&self, props: LinkProps) {
143 self.0.borrow_mut().link.push(rsx! {
144 link {
145 rel: props.rel,
146 media: props.media,
147 title: props.title,
148 disabled: props.disabled,
149 r#as: props.r#as,
150 sizes: props.sizes,
151 href: props.href,
152 crossorigin: props.crossorigin,
153 referrerpolicy: props.referrerpolicy,
154 fetchpriority: props.fetchpriority,
155 hreflang: props.hreflang,
156 integrity: props.integrity,
157 r#type: props.r#type,
158 blocking: props.blocking,
159 ..props.additional_attributes,
160 }
161 })
162 }
163
164 fn create_head_component(&self) -> bool {
165 self.warn_if_streaming();
166 self.serialize_for_hydration();
167 true
168 }
169}