dioxus_server/config.rs
1//! Configuration for how to serve a Dioxus application
2#![allow(non_snake_case)]
3
4use dioxus_core::LaunchConfig;
5use std::any::Any;
6use std::sync::Arc;
7
8use crate::{IncrementalRendererConfig, IndexHtml};
9
10#[allow(unused)]
11pub(crate) type ContextProviders = Arc<Vec<Box<dyn Fn() -> Box<dyn Any> + Send + Sync + 'static>>>;
12
13/// A ServeConfig is used to configure how to serve a Dioxus application. It contains information about how to serve static assets, and what content to render with [`dioxus_ssr`].
14#[derive(Clone)]
15pub struct ServeConfig {
16 pub(crate) index: IndexHtml,
17 pub(crate) incremental: Option<IncrementalRendererConfig>,
18 pub(crate) context_providers: Vec<Arc<dyn Fn() -> Box<dyn Any> + Send + Sync + 'static>>,
19 pub(crate) streaming_mode: StreamingMode,
20}
21
22/// The streaming mode to use while rendering the page
23#[derive(Clone, Copy, Default, PartialEq)]
24pub enum StreamingMode {
25 /// Streaming is disabled; all server futures should be resolved before hydrating the page on the client
26 #[default]
27 Disabled,
28
29 /// Out of order streaming is enabled; server futures are resolved out of order and streamed to the client
30 /// as they resolve
31 OutOfOrder,
32}
33
34impl LaunchConfig for ServeConfig {}
35
36impl Default for ServeConfig {
37 fn default() -> Self {
38 Self::new()
39 }
40}
41
42impl ServeConfig {
43 /// Create a new ServeConfig with incremental static generation disabled and the default index.html settings.
44 pub fn builder() -> Self {
45 Self::new()
46 }
47
48 /// Create a new ServeConfig with incremental static generation disabled and the default index.html settings
49 ///
50 /// This will automatically use the `index.html` file in the `/public` directory if it exists.
51 /// The `/public` folder is meant located next to the current executable. If no `index.html` file is found,
52 /// a default index.html will be used, which will not include any JavaScript or WASM initialization code.
53 ///
54 /// To provide an alternate `index.html`, you can use `with_index_html` method instead.
55 pub fn new() -> Self {
56 let index = if let Some(public_path) = crate::public_path() {
57 let index_html_path = public_path.join("index.html");
58
59 if index_html_path.exists() {
60 let index_html = std::fs::read_to_string(index_html_path)
61 .expect("Failed to read index.html from public directory");
62
63 IndexHtml::new(&index_html, "main")
64 .expect("Failed to parse index.html from public directory")
65 } else {
66 IndexHtml::ssr_only()
67 }
68 } else {
69 tracing::warn!(
70 "Cannot identify public directory, using default index.html. If you need client-side scripts (like JS + WASM), please provide an explicit public directory."
71 );
72 IndexHtml::ssr_only()
73 };
74
75 Self {
76 index,
77 incremental: None,
78 context_providers: Default::default(),
79 streaming_mode: StreamingMode::default(),
80 }
81 }
82
83 /// Create a new ServeConfig with the given parsed `IndexHtml` structure.
84 ///
85 /// You can create the `IndexHtml` structure by using `IndexHtml::new` method, or manually from
86 /// a string or file.
87 pub fn with_index_html(index: IndexHtml) -> Self {
88 Self {
89 index,
90 incremental: Default::default(),
91 context_providers: Default::default(),
92 streaming_mode: Default::default(),
93 }
94 }
95
96 /// Enable incremental static generation. Incremental static generation caches the
97 /// rendered html in memory and/or the file system. It can be used to improve performance of heavy routes.
98 ///
99 /// ```rust, no_run
100 /// # fn app() -> Element { unimplemented!() }
101 /// use dioxus::prelude::*;
102 ///
103 /// // Finally, launch the app with the config
104 /// LaunchBuilder::new()
105 /// // Only set the server config if the server feature is enabled
106 /// .with_cfg(server_only!(dioxus_server::ServeConfig::default().incremental(dioxus_server::IncrementalRendererConfig::default())))
107 /// .launch(app);
108 /// ```
109 pub fn incremental(mut self, cfg: IncrementalRendererConfig) -> Self {
110 self.incremental = Some(cfg);
111 self
112 }
113
114 /// Provide context to the root and server functions. You can use this context while rendering with [`consume_context`](dioxus_core::consume_context).
115 ///
116 ///
117 /// The context providers passed into this method will be called when the context type is requested which may happen many times in the lifecycle of the application.
118 ///
119 ///
120 /// Context will be forwarded from the LaunchBuilder if it is provided.
121 ///
122 /// ```rust, no_run
123 /// #![allow(non_snake_case)]
124 /// use dioxus::prelude::*;
125 /// use std::sync::Arc;
126 /// use std::any::Any;
127 ///
128 /// fn main() {
129 /// // Hydrate the application on the client
130 /// #[cfg(not(feature = "server"))]
131 /// dioxus::launch(app);
132 ///
133 /// #[cfg(feature = "server")]
134 /// dioxus_server::serve(|| async move {
135 /// use dioxus_server::{axum, ServeConfig, DioxusRouterExt};
136 ///
137 /// let config = ServeConfig::default()
138 /// // You can provide context to your whole app on the server (including server functions) with the `context_provider` method on the launch builder
139 /// .context_providers(Arc::new(vec![Box::new(|| Box::new(1234u32) as Box<dyn Any>) as Box<dyn Fn() -> Box<dyn Any> + Send + Sync>]));
140 ///
141 /// Ok(
142 /// axum::Router::new()
143 /// .serve_dioxus_application(config, app)
144 /// )
145 /// })
146 /// }
147 ///
148 /// #[server]
149 /// async fn read_context() -> ServerFnResult<u32> {
150 /// Ok(123)
151 /// }
152 ///
153 /// fn app() -> Element {
154 /// let future = use_resource(read_context);
155 /// rsx! {
156 /// h1 { "{future:?}" }
157 /// }
158 /// }
159 /// ```
160 pub fn context_providers(mut self, state: ContextProviders) -> Self {
161 // This API should probably accept Vec<Box<dyn Fn() -> Box<dyn Any> + Send + Sync + 'static>> instead of Arc so we can
162 // continue adding to the context list after calling this method. Changing the type is a breaking change so it cannot
163 // be done until 0.7 is released.
164 let context_providers = (0..state.len()).map(|i| {
165 let state = state.clone();
166 Arc::new(move || state[i]()) as Arc<dyn Fn() -> Box<dyn std::any::Any> + Send + Sync>
167 });
168 self.context_providers.extend(context_providers);
169 self
170 }
171
172 /// Provide context to the root and server functions. You can use this context
173 /// while rendering with [`consume_context`](dioxus_core::consume_context).
174 ///
175 ///
176 /// The context providers passed into this method will be called when the context type is requested which may happen many times in the lifecycle of the application.
177 ///
178 ///
179 /// Context will be forwarded from the LaunchBuilder if it is provided.
180 ///
181 /// ```rust, no_run
182 /// #![allow(non_snake_case)]
183 /// use dioxus::prelude::*;
184 ///
185 /// fn main() {
186 /// #[cfg(not(feature = "server"))]
187 /// // Hydrate the application on the client
188 /// dioxus::launch(app);
189 ///
190 /// #[cfg(feature = "server")]
191 /// dioxus_server::serve(|| async move {
192 /// use dioxus_server::{axum, ServeConfig, DioxusRouterExt};
193 ///
194 /// let config = ServeConfig::default()
195 /// // You can provide context to your whole app on the server (including server functions) with the `context_provider` method on the launch builder
196 /// .context_provider(|| 1234u32);
197 ///
198 /// Ok(
199 /// axum::Router::new()
200 /// .serve_dioxus_application(config, app)
201 /// )
202 /// });
203 /// }
204 ///
205 /// #[server]
206 /// async fn read_context() -> ServerFnResult<u32> {
207 /// Ok(123)
208 /// }
209 ///
210 /// fn app() -> Element {
211 /// let future = use_resource(read_context);
212 /// rsx! {
213 /// h1 { "{future:?}" }
214 /// }
215 /// }
216 /// ```
217 pub fn context_provider<C: 'static>(
218 mut self,
219 state: impl Fn() -> C + Send + Sync + 'static,
220 ) -> Self {
221 self.context_providers
222 .push(Arc::new(move || Box::new(state())));
223 self
224 }
225
226 /// Provide context to the root and server functions. You can use this context while rendering with [`consume_context`](dioxus_core::consume_context).
227 ///
228 /// Context will be forwarded from the LaunchBuilder if it is provided.
229 ///
230 /// ```rust, no_run
231 /// #![allow(non_snake_case)]
232 /// use dioxus::prelude::*;
233 ///
234 /// fn main() {
235 /// // Hydrate the application on the client
236 /// #[cfg(not(feature = "server"))]
237 /// dioxus::launch(app);
238 ///
239 /// // Run a custom server with axum on the server
240 /// #[cfg(feature = "server")]
241 /// dioxus_server::serve(|| async move {
242 /// use dioxus_server::{axum, ServeConfig, DioxusRouterExt};
243 ///
244 /// let config = ServeConfig::default()
245 /// // You can provide context to your whole app on the server (including server functions) with the `context_provider` method on the launch builder
246 /// .context(1234u32);
247 ///
248 /// Ok(
249 /// axum::Router::new()
250 /// .serve_dioxus_application(config, app)
251 /// )
252 /// });
253 /// }
254 ///
255 /// #[server]
256 /// async fn read_context() -> ServerFnResult<u32> {
257 /// Ok(123)
258 /// }
259 ///
260 /// fn app() -> Element {
261 /// let future = use_resource(read_context);
262 /// rsx! {
263 /// h1 { "{future:?}" }
264 /// }
265 /// }
266 /// ```
267 pub fn context(mut self, state: impl Any + Clone + Send + Sync + 'static) -> Self {
268 self.context_providers
269 .push(Arc::new(move || Box::new(state.clone())));
270 self
271 }
272
273 /// Set the streaming mode for the server. By default, streaming is disabled.
274 ///
275 /// ```rust, no_run
276 /// # use dioxus::prelude::*;
277 /// # fn app() -> Element { unimplemented!() }
278 /// dioxus::LaunchBuilder::new()
279 /// .with_context(server_only! {
280 /// dioxus::server::ServeConfig::builder().streaming_mode(dioxus::server::StreamingMode::OutOfOrder)
281 /// })
282 /// .launch(app);
283 /// ```
284 pub fn streaming_mode(mut self, mode: StreamingMode) -> Self {
285 self.streaming_mode = mode;
286 self
287 }
288
289 /// Enable out of order streaming. This will cause server futures to be resolved out of order and streamed to the client as they resolve.
290 ///
291 /// It is equivalent to calling `streaming_mode(StreamingMode::OutOfOrder)`
292 ///
293 /// ```rust, no_run
294 /// # use dioxus::prelude::*;
295 /// # fn app() -> Element { unimplemented!() }
296 /// dioxus::LaunchBuilder::new()
297 /// .with_context(server_only! {
298 /// dioxus::server::ServeConfig::builder().enable_out_of_order_streaming()
299 /// })
300 /// .launch(app);
301 /// ```
302 pub fn enable_out_of_order_streaming(mut self) -> Self {
303 self.streaming_mode = StreamingMode::OutOfOrder;
304 self
305 }
306}