๐ Blog Publishing
Load markdown posts with frontmatter and render to SEO-friendly HTML.
๐ฏ Overview
| Class | Purpose |
|---|---|
PostLoader |
Load markdown posts from filesystem |
MarkdownEngine |
Render markdown to SEO-friendly HTML |
๐ PostLoader
| Method | Purpose |
|---|---|
.load_posts() |
Load all posts sorted by date |
.get_post() |
Get single post by slug |
PostLoader
def PostLoader(
posts_dir:str, # Directory containing .md files
):
Load and parse markdown blog posts from filesystem
PostLoader.load_posts
def load_posts(
)->List[Dict]:
Load all markdown posts from directory.
Returns list of post dicts sorted by date (newest first). Each post contains: title, date, slug, body, categories, author, series.
Example: ```python loader = PostLoader(โblog/postsโ) posts = loader.load_posts()
for post in posts:
print(f"{post['title']} - {post['slug']}")
```
PostLoader.get_post
def get_post(
slug:str
)->Optional[Dict]: # URL slug (e.g., 'my-post')
Get single post by slug.
Example: python post = loader.get_post('bg0010') if post: print(post['title'])
๐จ MarkdownEngine
| Method | Purpose |
|---|---|
.render() |
Convert markdown to HTML |
MarkdownEngine
def MarkdownEngine(
):
Render markdown to HTML with SEO extensions
MarkdownEngine.render
def render(
content:str
)->str: # Markdown content
Convert markdown to HTML.
Returns HTML string with proper semantic tags for SEO.
Example:
```python
engine = MarkdownEngine()
html = engine.render('# Hello
This is bold.โ) print(html) #
Hello
This is bold.
```
MarkdownEngine.get_toc
def get_toc(
)->str:
Get table of contents HTML from last render.
Must call render() first. Returns empty string if no headings.
Example:
```python
engine = MarkdownEngine()
html = engine.render('# Title
Section 1
Section 2โ)
toc = engine.get_toc()
print(toc) # <ul><li><a href="#section-1">Section 1</a>...</li></ul>
```
FastHTML Integration Example
Basic pattern for server-side rendering with FastHTML:
from fasthtml.common import *
from fh_saas.utils_blog import PostLoader, MarkdownEngine
app = FastHTML()
loader = PostLoader('blog/posts')
engine = MarkdownEngine()
@app.get('/blog')
def blog_index():
posts = loader.load_posts()
return Titled('Blog',
*[Article(
H2(A(p['title'], href=f"/blog/{p['slug']}")),
P(p['description']),
Small(p['date'].strftime('%Y-%m-%d') if p['date'] else '')
) for p in posts]
)
@app.get('/blog/{slug}')
def blog_post(slug: str):
post = loader.get_post(slug)
if not post:
return 'Post not found', 404
html_content = engine.render(post['body'])
toc = engine.get_toc()
return Titled(post['title'],
Article(
NotStr(toc), # Table of contents
NotStr(html_content) # Rendered markdown
)
)