Dynamic Blogroll for Bearblog (and other sites)
Recently, an anonymous bird made a bold claim on Discord:

Comparing Anne's blog over on DIY & Dragons to social media isn't an exaggeration. You can find curated lists of active blogs for the TTRPG hobby thereβa collection of links to the latest posts from numerous amazing blogs. This provides you with direction and permission to discover more content elsewhere. That's why links are important. That's why links are powerful. And that's why big platforms gaslight users into thinking that using links is bad and should be punished.
The blogroll feature is, in my opinion, the biggest (and perhaps only) reason to use Google's Blogger platform. It made me ask: How can I create a similar feature for other websites? Essentially, I wanted a system that dynamically generates a curated list of recent posts from other blogs, without requiring manual updates.
Inspired by my previous post about adding Bluesky as comments and its JavaScript-based approach, I realized I might use my FreshRSS instance. FreshRSS has an API, so why not leverage that?
That idea quickly grew into a prototype. After two weeks of refinement, it's now stable enough to share. Ready to see the results? Check out my Blogroll. If you want to build your own, keep reading!
β± WARNING
This approach is quite technical and may not fit every scenario. While it's as low-code as possible, you need a basic understanding of the following:
- How to host applications on servers.
- Basic proficiency with command-line tools.
- HTML and CSS editing for your web pages.
Prerequisites
To create a similar setup, you'll need:
- Access to a FreshRSS API
- Hosting Capability for a Python Flask Application
- A paid Bearblog account (only if you plan to use Bearblog)
You can use this approach on any site where you can edit HTML and CSS, such as Blot or other platforms.
Why a Python application? It acts as a secure proxy between your webpage and FreshRSS, ensuring your credentials remain private and simplifying API calls.
β± NOTE
If you're using a different RSS reader that mimics the old Google Reader API, it might work as well. However, I've only tested this with FreshRSS.
Getting Started
Step 1: Set an API password
- Go to your FreshRSS profile.
- Scroll down to External access via API.
- Set an API password.
Step 2: Retrieve the API Token
Run the following command in your terminal, replacing <your-freshrss-url>
, <your-user-name>
, and <your-api-password>
:
curl 'https://<your-freshrss-url>/api/greader.php/accounts/ClientLogin?Email=<your-user-name>&Passwd=<your-api-password>'
The response should look like this:
SID=<username>/<hash>
LSID=null
Auth=<username>/<hash>
Copy the <username>/<hash>
value. You'll use it in your proxy configuration.
Setup Proxy
I developed FreshProxy specifically for this purpose. Detailed instructions are available in the repository, but here's a concise guide.
Log in to the server where you'll host FreshProxy to get started.
Step 1: Clone the Repository
git clone https://github.com/hstct/FreshProxy.git
cd FreshProxy
Step 2: Create and Configure an Environment File
Use the example file to get started:
cp .env.example .env
Open .env
and edit these variables:
FRESHRSS_API_TOKEN=<your-api-token>
FRESHRSS_BASE_URL=https://<your-freshrss-url>/api/greader.php/reader/api/0
FRESHPROXY_ALLOWED_ORIGINS=<your-blog-url>
FRESHRSS_API_TOKEN
: The token you copied earlier.FRESHRSS_BASE_URL
: Your FreshRSS API base URL.FRESHPROXY_ALLOWED_ORIGINS
: Your blog's URL(s).
For additional details, see the README.
Step 3: Run the Proxy Application on Your Server
Using a container (Docker or Podman) is simplest:
docker build -t freshproxy .
docker run -p 8000:8000 \
-e FRESHRSS_API_TOKEN="<your-api-token>" \
-e FRESHRSS_BASE_URL="https://<your-freshrss-url>/api/greader.php/reader/api/0" \
-e FRESHPROXY_ALLOWED_ORIGIN="<your-blog-url>,<your-other-blog-url>" \
--name freshproxy \
--detach
freshproxy
You can start/stop the container:
docker stop freshproxy
docker start freshproxy
Alternatively, you can run the application directly (given you have gunicorn
installed):
gunicorn --bind 0.0.0.0:8000 freshproxy.wsgi:app --worker-class sync --workers 4 --daemon --pid /tmp/freshproxy.pid
You can stop the process like this:
kill $(cat /tmp/freshproxy.pid)
β± NOTE
Some hosting providers may require a different port or configuration. Adjust accordingly.
Step 4: Test the Proxy
Check if it's running correctly:
curl "https://<your-proxy-url>/digest"
If successful, you'll receive valid JSON data.
Prepare your Client
To reduce your workload, I created a JavaScript package called blogroller. You can import and configure it to fetch and display your blogroll. The default styling is neutral, but you can customize it with CSS.
Step 1: Add stylesheet
In your <head>
:
<head>
<!-- Add to the end of your header -->
<link rel="stylesheet" href="https://unpkg.com/blogroller@latest/dist/blogroller.css" />
</head>
Step 2: Load the Script
Place this in your <footer>
or anywhere on your page:
< script type="module">
import { Blogroll } from 'https://unpkg.com/blogroller@latest/dist/blogroller.esm.js';
document.addEventListener('DOMContentLoaded', function () {
if (document.body.classList.contains('blogroll')) {
const blogroll = new Blogroll();
blogroll.initialize({
proxyUrl: 'https://<url-to-your-proxy>',
categoryLabel: '<name-of-the-freshrss-feed-category>',
containerId: 'rss-feed',
batchSize: 10,
});
}
});
</script>
Key Configuration Options:
proxyUrl
: Your proxy server's URL (required).categoryLabel
: The FreshRSS feed category to display (required).containerId
: The element where the blogroll is rendered (optional).batchSize
: Number of posts fetched at a time (optional).
Step 3: Prepage Your Blogroll Page
If your <body>
tag has class="blogroll"
, the script will only run on that page. For Bearblog users, be sure to specify the attribute class_name: blogroll
when creating a new page.
Anywhere you want the blogroll to appear:
<div id="rss-widget">
<div id="rss-feed"></div>
</div>
β± NOTE
If you change containerId
in the initialization, update the corresponding id
in your HTML as well. You can use multiple containers (and categories) on one page by replicating this pattern.
Step 4: Customize Styling (Optional)
To customize the colors, add the following CSS variables to your stylesheet's :root
and change the values to your liking:
:root {
--blogroller-border-color: #3c3836;
--blogroller-feed-title-color: #d79921;
--blogroller-feed-title-hover-bg: #d79921;
--blogroller-feed-title-hover-color: #282828;
--blogroller-link-color: #83a598;
--blogroller-link-hover-color: #282828;
--blogroller-text-italic-color: #98971a;
--blogroller-text-italic-hl-color: #b8bb26;
}
Alternatively, if you prefer, you can style each part of the blogroll using the unique blogroller-*
class prefixes provided by the blogroller package.
Closing
I hope this guide helps you create a dynamic blogroll. Links are powerful tools for discoveryβsharing your favorite blogs spreads valuable reading lists across the community!
If you try this, I'd love to hear how it went. Feel free to reach out or share your blogroll. Next time, I promise more TTRPG-related content again!