Skip to main content

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}