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}