fh_posts is a Python package that transforms your Markdown and Jupyter Notebook blog posts into dynamic FastHTML content. It extracts YAML front matter using fastcore’s AttrDict for seamless metadata access and executes custom-tagged Python code blocks to render interactive posts.
Features
- YAML Front Matter Parsing: Automatically extracts front matter (e.g., title, summary, date, tags) and makes it accessible via both dictionary and attribute notation.
- Multi-Format Support: Process both Markdown (.md) and Jupyter Notebook (.ipynb) files.
- Dynamic Code Execution: Custom code block tags let you decide whether to run code, hide code, or show output live.
- FastHTML Integration: Render posts to FastHTML’s NotStr objects for lightning-fast blog content.
- Link Handling: Optionally set all links to open in a new window when rendering.
Installation
Install the package via pip:
Usage
Learn more about the package in the documentation.
Check out this blog post for a demonstration of the package in action.
Import the package:
from fh_posts.core import load_posts
Tag your code blocks (explained below):
```python:run
print("Hello, world!")
```
Load the posts and render them:
# Load posts from the 'posts' directory posts = load_posts('posts') # Access metadata for post in posts: print(post.title, post.date) # Render a post by its slug post = next(p for p in posts if p.slug == 'hello') html_output = post.render(open_links_new_window=True) print(html_output)
Tags
In a markdown file when you add a code block with triple backticks and python, append additional colon seperated tags to control how the code is run and rendered. All run code blocks will be executed in order and be in the namespace for the rest of the post. This should feel familiar to those who have used jupyter notebooks.
python(default) - output the code but don’t run itpython:run- run and show the code and the outputpython:run:hide- run the code but don’t show the code or outputpython:run:hide-in- run the code but don’t show the code block, only the outputpython:run:hide-out- run the code and show the output but don’t show the code blockpython:run:hide-call- run the code and show the output and the code block but don’t show the call to the function (last line of code)
In a notebook file all code cells are run by default. Add a #|python
tag to the first line of any code cell to also have it appear as a code
block in the post. All of the other tags for markdown posts apply to
notebook posts as well. Having to add run each time is redundant since
all cells are run but it keeps things consistent between markdown and
notebook posts.
Example: Markdown Posts
Markdown File (hello.md):
--- title: Hello FastHTML and MonsterUI summary: An introduction to FastHTML and MonsterUI. date: February 25, 2025 tags: - python - fasthtml - monsterui --- Welcome to our blog post! ```python:run print("Hello, world!") ```
Loading & Rendering:
from fh_posts.core import load_posts # Load posts from the 'posts' directory posts = load_posts('posts') # Access metadata for post in posts: print(post.title, post.date) # Render a post by its slug post = next(p for p in posts if p.slug == 'hello') html_output = post.render(open_links_new_window=True) print(html_output)
Example: Jupyter Notebook Posts
Notebook File (notebook_post.ipynb):
- Cell 1 (Raw Cell with YAML Front Matter):
--- title: Notebook Post Example summary: A demonstration of a notebook-based blog post. date: March 1, 2025 tags: - jupyter - python ---
Code Cell Example:
#|python:run:hide-in print("Notebook live output")
Contributing
Contributions are welcome! Please open an issue or submit a pull request to help improve fh_posts.
