Skip to main content

leptos_md/
lib.rs

1//! # leptos-md
2//!
3//! A simple, signal-free Markdown renderer for [Leptos](https://leptos.dev) with Tailwind CSS styling.
4//!
5//! ## Quick Start
6//!
7//! ```rust,ignore
8//! use leptos::prelude::*;
9//! use leptos_md::Markdown;
10//!
11//! #[component]
12//! fn App() -> impl IntoView {
13//!     view! {
14//!         <Markdown content="# Hello World\n\nThis is **markdown**!" />
15//!     }
16//! }
17//! ```
18//!
19//! ## Features
20//!
21//! - **Dead simple API** - `<Markdown content=md />` and you're done
22//! - **Beautiful by default** - Tailwind prose styling with dark mode support
23//! - **GitHub Flavored Markdown** - Tables, task lists, strikethrough, footnotes
24//! - **Code block themes** - Built-in Tailwind themes (GitHub, Monokai, Dark, Light)
25//! - **External highlighter ready** - Outputs `language-xxx` classes for Prism.js, highlight.js
26//! - **SSR ready** - Works seamlessly with Leptos server-side rendering
27//!
28//! ## Customization
29//!
30//! Use [`MarkdownOptions`] for fine-grained control:
31//!
32//! ```rust,ignore
33//! use leptos_md::{Markdown, MarkdownOptions, CodeBlockTheme};
34//!
35//! let options = MarkdownOptions::new()
36//!     .with_gfm(true)
37//!     .with_code_theme(CodeBlockTheme::GitHub)
38//!     .with_language_classes(true)
39//!     .with_new_tab_links(true);
40//!
41//! view! {
42//!     <Markdown content="# Hello" options=options />
43//! }
44//! ```
45
46use leptos::prelude::*;
47
48mod components;
49mod renderer;
50
51pub use components::{
52    get_code_theme_classes, get_enhanced_prose_classes, CodeBlockTheme, MarkdownClasses,
53    MarkdownOptions, MarkdownStyles,
54};
55pub use renderer::MarkdownRenderer;
56
57/// Main component for rendering Markdown content with Tailwind CSS styling
58#[component]
59pub fn Markdown(
60    /// The markdown content as a string
61    #[prop(into)]
62    content: String,
63    /// Optional CSS class for the wrapper (will be combined with Tailwind prose classes)
64    #[prop(optional)]
65    class: Option<String>,
66    /// Markdown rendering options
67    #[prop(optional)]
68    options: Option<MarkdownOptions>,
69) -> impl IntoView {
70    let renderer = MarkdownRenderer::new(options.unwrap_or_default());
71
72    match renderer.render(&content) {
73        Ok(rendered_content) => {
74            let base_classes = get_enhanced_prose_classes();
75            let wrapper_class = match class {
76                Some(c) => format!("{} {}", base_classes, c),
77                None => base_classes.to_string(),
78            };
79
80            view! {
81                <div class=wrapper_class>
82                    {rendered_content}
83                </div>
84            }
85            .into_any()
86        }
87        Err(err) => {
88            leptos::logging::error!("Failed to render markdown: {}", err);
89            view! {
90                <div class="bg-red-50 dark:bg-red-950/30 border border-red-200 dark:border-red-800 rounded-lg p-4 text-red-800 dark:text-red-200">
91                    <p class="font-medium">"Failed to render markdown content"</p>
92                    <p class="text-sm mt-1">{err}</p>
93                </div>
94            }.into_any()
95        }
96    }
97}
98
99/// Utility function to render markdown string directly to AnyView with Tailwind styling
100pub fn render_markdown_string(content: &str) -> Result<AnyView, String> {
101    let renderer = MarkdownRenderer::new(MarkdownOptions::default());
102    renderer.render(content)
103}
104
105/// Utility function to render markdown with custom options and Tailwind styling
106pub fn render_markdown_with_options(
107    content: &str,
108    options: MarkdownOptions,
109) -> Result<AnyView, String> {
110    let renderer = MarkdownRenderer::new(options);
111    renderer.render(content)
112}