Rust library to generate static websites
5
fork

Configure Feed

Select the types of activity you want to include in your feed.

feat: rest of article

+227 -4
+7 -4
website/content/docs/images.md
··· 53 53 Images added to pages can be transformed by using [`ctx.assets.add_image_with_options()`](https://docs.rs/maudit/latest/maudit/assets/struct.RouteAssets.html#method.add_image_with_options), which takes an additional [`ImageOptions`](https://docs.rs/maudit/latest/maudit/assets/struct.ImageOptions.html) struct to specify how the image should be processed. 54 54 55 55 ```rs 56 + use maud::html; 56 57 use maudit::route::prelude::*; 57 58 58 59 #[route("/image")] ··· 63 64 let image = ctx.assets.add_image_with_options( 64 65 "path/to/image.jpg", 65 66 ImageOptions { 66 - width: Some(800), 67 - height: None, 68 - format: Some(ImageFormat::Png) 67 + width: Some(800), 68 + height: None, 69 + format: Some(ImageFormat::Png), 69 70 }, 70 71 )?; 71 72 72 - Ok(format!("<img src=\"{}\" alt=\"Processed Image\" />", image.url)) 73 + Ok(html! { 74 + (image.render("My 800 pixel wide PNG")) 75 + }) 73 76 } 74 77 } 75 78 ```
+220
website/content/news/2026-in-the-cursed-lands.md
··· 11 11 12 12 > Interested in trying out Maudit? Follow our [Quick Start](/docs/quick-start/) guide. 13 13 14 + ## Image processing 15 + 16 + [Maudit 0.4.0](https://github.com/bruits/maudit/blob/main/crates/maudit/CHANGELOG.md#040) added support for image processing, allowing you to easily resize, convert and optimize images for your website at build-time. 17 + 18 + ```rs 19 + use maud::html; 20 + use maudit::route::prelude::*; 21 + 22 + #[route("/image")] 23 + pub struct ImagePage; 24 + 25 + impl Route for ImagePage { 26 + fn render(&self, ctx: &mut PageContext) -> impl Into<RenderResult> { 27 + let image = ctx.assets.add_image_with_options( 28 + "path/to/image.jpg", 29 + ImageOptions { 30 + width: Some(800), 31 + height: None, 32 + format: Some(ImageFormat::Png), 33 + }, 34 + )?; 35 + 36 + Ok(html! { 37 + (image.render("My 800 pixel wide PNG")) 38 + }) 39 + } 40 + } 41 + ``` 42 + 43 + See [our section on image processing](https://maudit.org/docs/images/#processing-images) for more information on how to use images in Maudit. 44 + 45 + ### Placeholders generation 46 + 47 + Maudit also includes the ability to easily create low-quality image placeholders (LQIP) for your images using [ThumbHash](https://evanw.github.io/thumbhash/). 48 + 49 + ```rs 50 + use maudit::route::prelude::*; 51 + 52 + #[route("/image")] 53 + pub struct ImagePage; 54 + 55 + impl Route for ImagePage { 56 + fn render(&self, ctx: &mut PageContext) -> impl Into<RenderResult> { 57 + let image = ctx.assets.add_image("path/to/image.jpg")?; 58 + let placeholder = image.placeholder(); 59 + 60 + Ok(format!("<img src=\"{}\" alt=\"Image with placeholder\" style=\"background-image: url('{}'); background-size: cover;\" />", image.url(), placeholder.data_uri())) 61 + } 62 + } 63 + ``` 64 + 65 + Check [our documentation on placeholders](/docs/images/#placeholders) for more information. 66 + 67 + ## Customizable Markdown rendering 68 + 69 + [Maudit 0.5.0](https://github.com/bruits/maudit/blob/main/crates/maudit/CHANGELOG.md#050) added support for components and shortcodes in Markdown files. These features allows you to completely customize how your Markdown files are rendered and enhance them with cool new possibilities. 70 + 71 + ### Shortcodes 72 + 73 + Embedding a Youtube video typically requires one to copy this long, ugly, iframe tag and configure the different attributes to make sure it renders properly, it'd be nice to have something more friendly, a code that would be short, you will. 74 + 75 + ```md 76 + Here's my cool video: 77 + 78 + {{ youtube id="b_KfnGBtVeA" /}} 79 + ``` 80 + 81 + ```rs 82 + content_sources![ 83 + "articles" => glob_markdown_with_options::<ArticleContent>("content/articles/*.md", MarkdownOptions { 84 + shortcodes: { 85 + let mut shortcodes = MarkdownShortcodes::default(); 86 + 87 + shortcodes.register("youtube", |attrs, _| { 88 + if let Some(id) = attrs.get::<String>("id") { 89 + format!(r#"<iframe width="560" height="315" src="https://www.youtube.com/embed/{}" frameborder="0" allowfullscreen></iframe>"#, id) 90 + } else { 91 + panic!("YouTube shortcode requires an 'id' attribute"); 92 + } 93 + }); 94 + 95 + shortcodes 96 + }, 97 + ..Default::default() 98 + }) 99 + ], 100 + ``` 101 + 102 + For more information, read [our section on shortcodes](/docs/content/#shortcodes). 103 + 104 + ### Components 105 + 106 + Sometimes, you want to be able to keep writing normal, spec-compliant, Markdown, but still be able to add a bit of spice to it. For this Maudit supports components, allowing you to use custom code when rendering normal Markdown elements. 107 + 108 + For instance, you may want to add an anchor icon to every heading, without needing to use a `{{ heading }}` shortcode. 109 + 110 + ```rs 111 + use maudit::components::MarkdownComponents; 112 + 113 + struct CustomHeading; 114 + 115 + impl HeadingComponent for CustomHeading { 116 + fn render_start(&self, level: u8, id: Option<&str>, _classes: &[&str]) -> String { 117 + let id_attr = id.map(|i| format!(" id=\"{}\"", i)).unwrap_or_default(); 118 + let href = id.map(|i| format!("#{}", i)).unwrap_or_default(); 119 + format!( 120 + "<div><a href=\"{href}\"><span aria-hidden=\"true\">{}</span></a><h{level}{id_attr}>", include_str("icons/anchor.svg") 121 + ) 122 + } 123 + 124 + fn render_end(&self, level: u8) -> String { 125 + format!("</h{level}></div>") 126 + } 127 + } 128 + ``` 129 + 130 + ```rs 131 + content_sources![ 132 + "blog" => glob_markdown_with_options::<BlogPost>("content/blog/**/*.md", MarkdownOptions { 133 + components: MarkdownComponents::new().heading(CustomHeading), 134 + ..Default::default() 135 + }), 136 + ], 137 + ``` 138 + 139 + For more information, read [our section on components](/docs/content/#components). 140 + 141 + ## Improved error handling 142 + 143 + [Maudit 0.6.0](https://github.com/bruits/maudit/releases/tag/maudit-v0.6.0) and [0.6.6](https://github.com/bruits/maudit/releases/tag/maudit-v0.6.6) made it much easier to handle errors inside of pages by making all of the assets (which are quite prone to errors, filesystem and all) methods return Result instead of panicking. 144 + 145 + Additionally, pages themselves can now optionally return `Result` and will bubble up their errors up the chain up to [the entrypoint](/docs/entrypoint/) when using the `?` operator. Maudit implements `Into<RenderResult>` for `Result<T: Into<RenderResult>, E: Error>`, as such using `?` and returning `Result` require no signature changes inside your pages. 146 + 147 + ```rs 148 + use maudit::route::prelude::*; 149 + use maud::html; 150 + 151 + #[route("/example")] 152 + pub struct Example; 153 + 154 + impl Route for Example { 155 + fn render(&self, ctx: &mut PageContext) -> impl Into<RenderResult> { 156 + // Use the ? operator to bubble up asset-related errors 157 + let logo = ctx.assets.add_image("images/logo.png")?; 158 + 159 + // Wrap your return value with Ok() 160 + Ok(html! { 161 + (logo) 162 + p { "My cool logo!" } 163 + }) 164 + } 165 + } 166 + ``` 167 + 168 + Or, you can just `unwrap()` everything, that's ok! Check our section on [handling errors](/docs/routing/#handling-errors) if you'd like to learn more. 169 + 170 + ## Support for internationalization 171 + 172 + [Maudit 0.7.0](https://github.com/bruits/maudit/releases/tag/maudit-v0.7.0) added support for internationalizating routes. For instance, you may want to have a `/about` in English, but `/a-propos` and `/om-oss` in French and Swedish respectively. 173 + 174 + This is possible to do right now in Maudit: You can duplicate your `About` struct twice, register the two new routes, rewrite the `render` implementation twice.. but that's a bit cumbersome, so Maudit now allows you to generate all these pages using a single struct: 175 + 176 + ```rust 177 + use maudit::route::prelude::*; 178 + 179 + #[route( 180 + "/contact", 181 + locales(sv(prefix = "/sv"), de(path = "/de/kontakt")) 182 + )] 183 + pub struct Contact; 184 + 185 + impl Route for Contact { 186 + fn render(&self, ctx: &mut PageContext) -> impl Into<RenderResult> { 187 + match &ctx.variant { 188 + Some(language) => match language.as_str() { 189 + "sv" => "Kontakta oss.", 190 + "de" => "Kontaktieren Sie uns.", 191 + _ => unreachable!(), 192 + }, 193 + _ => "Contact us.", 194 + } 195 + } 196 + } 197 + ``` 198 + 199 + The ergonomics are still a bit iffy, but this nonetheless already makes it much easier to localize your website. To learn more about internationalization [visit our documentation](/docs/routing/#internationalization-i18n). 200 + 14 201 ## Built-in sitemap generation 15 202 16 203 [Maudit 0.9.0](https://github.com/bruits/maudit/releases/tag/maudit-v0.9.0) added support for automatically generating a sitemap for your website. In this new world of AI and other advanced web crawlers, sitemaps are a bit of an old relic. However, they're still considered useful to ensure that search engines properly index your website. ··· 54 241 <span class="text-center block italic">Showing the Hover strategy for prefetching</span> 55 242 56 243 For more information on prefetching, see [our prefetching documentation](/docs/prefetching/). 244 + 245 + ## Redirect utilities 246 + 247 + [Maudit 0.10.0](https://github.com/bruits/maudit/releases/tag/maudit-v0.10.0) also added a new `redirect()` function to... well, redirect to another page. 248 + 249 + ```rust 250 + use maudit::route::prelude::*; 251 + 252 + #[route("/redirect")] 253 + pub struct Redirect; 254 + 255 + impl Route for Redirect { 256 + fn render(&self, ctx: &mut PageContext) -> impl Into<RenderResult> { 257 + redirect("https://example.com") 258 + 259 + // Use a page's url method to generate type safe links: 260 + // redirect(&OtherPage.url(None)) 261 + } 262 + } 263 + ``` 264 + 265 + Simple enough. The return value of this function can be directly used in your pages, making it nice and easy to redirect to new content. To learn more about internationalization [redirect yourself to our documentation](/docs/routing/#redirects). 266 + 267 + ## The future 268 + 269 + Maudit is mightier than before, but there's still so many twisted paths we'd like to follow. Including, but not limited to: 270 + 271 + - Ability to generate variants of pages, outside of the localization system. 272 + - Support for generating PWAs automatically 273 + - Built-in font support (w/ subsetting) 274 + - ... and more! 275 + 276 + For now, we go back into hiding. See you soon!