This is unreleased documentation for Yew Next version.
For up-to-date documentation, see the latest version on docs.rs.

yew/
renderer.rs

1use std::cell::Cell;
2#[rustversion::since(1.81)]
3use std::panic::PanicHookInfo as PanicInfo;
4#[rustversion::before(1.81)]
5use std::panic::PanicInfo;
6use std::rc::Rc;
7
8use web_sys::Element;
9
10use crate::app_handle::AppHandle;
11use crate::html::BaseComponent;
12
13thread_local! {
14    static PANIC_HOOK_IS_SET: Cell<bool> = const { Cell::new(false) };
15}
16
17/// Set a custom panic hook.
18/// Unless a panic hook is set through this function, Yew will
19/// overwrite any existing panic hook when an application is rendered with [Renderer].
20#[cfg(feature = "csr")]
21pub fn set_custom_panic_hook(hook: Box<dyn Fn(&PanicInfo<'_>) + Sync + Send + 'static>) {
22    std::panic::set_hook(hook);
23    PANIC_HOOK_IS_SET.with(|hook_is_set| hook_is_set.set(true));
24}
25
26fn set_default_panic_hook() {
27    if std::thread::panicking() {
28        // very unlikely, but avoid hitting this when running parallel tests.
29        return;
30    }
31    if !PANIC_HOOK_IS_SET.with(|hook_is_set| hook_is_set.replace(true)) {
32        std::panic::set_hook(Box::new(console_error_panic_hook::hook));
33    }
34}
35
36/// The Yew Renderer.
37///
38/// This is the main entry point of a Yew application.
39#[cfg(feature = "csr")]
40#[derive(Debug)]
41#[must_use = "Renderer does nothing unless render() is called."]
42pub struct Renderer<COMP>
43where
44    COMP: BaseComponent + 'static,
45{
46    root: Element,
47    props: COMP::Properties,
48}
49
50impl<COMP> Default for Renderer<COMP>
51where
52    COMP: BaseComponent + 'static,
53    COMP::Properties: Default,
54{
55    fn default() -> Self {
56        Self::with_props(Default::default())
57    }
58}
59
60impl<COMP> Renderer<COMP>
61where
62    COMP: BaseComponent + 'static,
63    COMP::Properties: Default,
64{
65    /// Creates a [Renderer] that renders into the document body with default properties.
66    pub fn new() -> Self {
67        Self::default()
68    }
69
70    /// Creates a [Renderer] that renders into a custom root with default properties.
71    pub fn with_root(root: Element) -> Self {
72        Self::with_root_and_props(root, Default::default())
73    }
74}
75
76impl<COMP> Renderer<COMP>
77where
78    COMP: BaseComponent + 'static,
79{
80    /// Creates a [Renderer] that renders into the document body with custom properties.
81    pub fn with_props(props: COMP::Properties) -> Self {
82        Self::with_root_and_props(
83            gloo::utils::document()
84                .body()
85                .expect("no body node found")
86                .into(),
87            props,
88        )
89    }
90
91    /// Creates a [Renderer] that renders into a custom root with custom properties.
92    pub fn with_root_and_props(root: Element, props: COMP::Properties) -> Self {
93        Self { root, props }
94    }
95
96    /// Renders the application.
97    pub fn render(self) -> AppHandle<COMP> {
98        set_default_panic_hook();
99        AppHandle::<COMP>::mount_with_props(self.root, Rc::new(self.props))
100    }
101}
102
103#[cfg(feature = "hydration")]
104mod feat_hydration {
105    use super::*;
106
107    impl<COMP> Renderer<COMP>
108    where
109        COMP: BaseComponent + 'static,
110    {
111        /// Hydrates the application.
112        pub fn hydrate(self) -> AppHandle<COMP> {
113            set_default_panic_hook();
114            AppHandle::<COMP>::hydrate_with_props(self.root, Rc::new(self.props))
115        }
116    }
117}