中文版本 | English Version
NamBlog allows you to add custom HTML content below the category list in the article page navigation panel (right-side collapsible panel) to display QR codes, ads, links, and other personalized widgets.
- � Third-party comment widgets (Giscus, Utterances, etc.)
- �💰 Donation/tip QR codes
- 📱 WeChat official account follow cards
- 📧 Email subscription entry
- 🔗 Friendly links
- 📺 Ad slots (iframe supported)
- 🎨 Any custom HTML content
Add the ArticleSidebarWidget field in data/config/config.json:
{
"Blog": {
"BlogName": "My Blog",
"Blogger": "Blogger",
"ArticleSidebarWidget": "<div style='text-align:center; padding:12px; background:rgba(243,244,246,0.5); border-radius:8px;'><h4 style='font-size:14px; margin:0 0 8px;'>❤️ Support</h4><img src='/images/donate-qrcode.png' style='width:120px; height:120px; border-radius:4px;' /><p style='font-size:12px; opacity:0.7; margin:8px 0 0;'>Thank you for your support</p></div>"
}
}Set environment variables when starting Docker Compose or container:
# docker-compose.yml
services:
namblog:
environment:
- Blog__ArticleSidebarWidget=<div style='text-align:center;'>...</div>Or command line:
docker run -e 'Blog__ArticleSidebarWidget=<div>...</div>' ...💡 Dark Mode Note: All examples below use relative transparency or system-adaptable colors that work well with dark/light theme switching.
{
"ArticleSidebarWidget": "<img src='/images/qrcode.png' style='width:120px; display:block; margin:0 auto;' />"
}{
"ArticleSidebarWidget": "<div style='text-align:center; padding:16px; background:rgba(243,244,246,0.5); border-radius:8px;'><h4 style='font-size:14px; font-weight:600; margin:0 0 12px;'>❤️ Support</h4><img src='/images/donate.png' style='width:100px; height:100px; border-radius:4px; margin:0 auto;' /><p style='font-size:12px; opacity:0.7; margin:8px 0 0;'>If this article helps you<br>Feel free to donate</p></div>"
}{
"ArticleSidebarWidget": "<div style='text-align:center; padding:14px; background:rgba(243,244,246,0.3); border:1px solid rgba(229,231,235,0.5); border-radius:8px; box-shadow:0 1px 3px rgba(0,0,0,0.05);'><h4 style='font-size:13px; font-weight:600; margin:0 0 10px;'>📱 Follow Us</h4><img src='/images/wechat-official.png' style='width:100px; height:100px; border-radius:4px; margin:0 auto 8px;' /><p style='font-size:11px; opacity:0.6; margin:0; line-height:1.5;'>Get latest article updates</p></div>"
}{
"ArticleSidebarWidget": "<div style='padding:14px; background:rgba(243,244,246,0.4); border-radius:8px;'><h4 style='font-size:13px; font-weight:600; margin:0 0 10px; border-bottom:2px solid currentColor; padding-bottom:6px; opacity:0.9;'>📚 Recommended</h4><a href='/article/vue-tutorial' style='display:block; padding:8px 0; text-decoration:none; font-size:13px; border-bottom:1px solid rgba(229,231,235,0.5); opacity:0.85;'>→ Vue 3 Tutorial</a><a href='/article/react-hooks' style='display:block; padding:8px 0; text-decoration:none; font-size:13px; border-bottom:1px solid rgba(229,231,235,0.5); opacity:0.85;'>→ React Hooks Guide</a><a href='/article/typescript-guide' style='display:block; padding:8px 0; text-decoration:none; font-size:13px; opacity:0.85;'>→ TypeScript Practical Guide</a></div>"
}{
"ArticleSidebarWidget": "<div style='padding:12px; background:rgba(254,243,199,0.3); border-left:3px solid rgba(245,158,11,0.6); border-radius:4px;'><p style='font-size:12px; margin:0 0 6px; font-weight:600; opacity:0.8;'>📖 Reading</p><p style='font-size:13px; margin:0; font-weight:500; opacity:0.9;'>{{articleTitle}}</p><a href='/donate?article={{articleSlug}}' style='display:inline-block; margin-top:8px; padding:4px 12px; background:rgba(245,158,11,0.2); border-radius:4px; font-size:11px; text-decoration:none; opacity:0.85;'>Support This Article</a></div>"
}{
"ArticleSidebarWidget": "<div style='text-align:center; padding:14px; background:rgba(249,250,251,0.5); border-radius:8px;'><h4 style='font-size:13px; font-weight:600; margin:0 0 10px;'>💰 Donate</h4><div style='display:flex; gap:10px; justify-content:center;'><div><img src='/images/wechat-pay.png' style='width:80px; height:80px; border-radius:4px;' /><p style='font-size:11px; opacity:0.7; margin:4px 0 0;'>WeChat</p></div><div><img src='/images/alipay.png' style='width:80px; height:80px; border-radius:4px;' /><p style='font-size:11px; opacity:0.7; margin:4px 0 0;'>Alipay</p></div></div></div>"
}{
"ArticleSidebarWidget": "<div style='padding:14px; background:rgba(236,253,245,0.4); border:1px solid rgba(16,185,129,0.3); border-radius:8px;'><h4 style='font-size:13px; font-weight:600; margin:0 0 8px; opacity:0.9;'>📧 Subscribe</h4><p style='font-size:12px; margin:0 0 10px; line-height:1.5; opacity:0.8;'>Receive latest tech articles weekly</p><a href='mailto:subscribe@example.com?subject=Subscribe' style='display:block; text-align:center; padding:8px; background:rgba(16,185,129,0.2); border-radius:4px; font-size:12px; font-weight:500; text-decoration:none; opacity:0.9;'>Subscribe Now</a></div>"
}{
"ArticleSidebarWidget": "<iframe src='https://ad-service.com/widget' style='width:100%; height:250px; border:0; display:block; border-radius:8px;'></iframe>"
}{
"ArticleSidebarWidget": "<div style='padding:12px; background:rgba(243,244,246,0.4); border-radius:8px;'><h4 style='font-size:13px; font-weight:600; margin:0 0 10px; opacity:0.9;'>💬 Comments</h4><p style='font-size:12px; opacity:0.7; margin:0 0 8px; line-height:1.5;'>Join the discussion on GitHub Discussions</p><a href='https://github.com/your-repo/discussions' target='_blank' style='display:block; text-align:center; padding:8px; background:rgba(37,99,235,0.1); border-radius:4px; font-size:12px; text-decoration:none; opacity:0.85;'>👉 Comment</a></div>"
}{
"ArticleSidebarWidget": "<div style='padding:12px; background:rgba(243,244,246,0.4); border-radius:8px;'><h4 style='font-size:13px; font-weight:600; margin:0 0 8px; opacity:0.9;'>💬 Discussion</h4><p style='font-size:12px; opacity:0.7; margin:0 0 8px;'>What do you think about \"{{articleTitle}}\"?</p><a href='https://github.com/your-repo/issues?q=is:issue+{{articleSlug}}' target='_blank' style='display:block; text-align:center; padding:8px; background:rgba(16,185,129,0.1); border-radius:4px; font-size:12px; text-decoration:none; opacity:0.85;'>Join Discussion</a></div>"
}You can use article metadata variables in the configuration, which will be automatically replaced during rendering:
{{articleTitle}}- Article title{{articleSlug}}- Article slug{{articleId}}- Article ID
Example:
{
"ArticleSidebarWidget": "<div style='padding:12px;'><p style='font-size:12px; opacity:0.7;'>Reading: <strong>{{articleTitle}}</strong></p><a href='/donate?article={{articleSlug}}' style='opacity:0.85;'>Support this article</a></div>"
}Since custom widgets are located in Shadow DOM, inline styles must be used for proper display.
Use transparency and rgba colors:
<div style="padding:12px; background:rgba(243,244,246,0.5); border-radius:8px;">
<h4 style="font-size:14px; margin:0 0 8px; opacity:0.9;">Title</h4>
<img src="/image.png" style="width:120px; display:block; margin:0 auto;" />
</div>Use currentColor and relative values:
<div style="padding:12px; border-bottom:2px solid currentColor; opacity:0.8;">
<a href="/link" style="opacity:0.85; text-decoration:none;">Link text</a>
</div><!-- External CSS classes won't work -->
<div class="my-custom-class">
<img src="/image.png" class="my-image" />
</div>
<!-- Hardcoded dark background, looks inconsistent in light mode -->
<div style="background:#1f2937; color:#fff;">
<p>This will look jarring in light mode</p>
</div>- Background Colors: Use
rgba()with transparency, e.g.,rgba(243,244,246,0.5) - Text Colors: Use
opacityto control transparency instead of hardcoded colors - Borders: Use
rgba()orcurrentColor - Links: Rely on system default link colors (auto-adapts to theme) or use
opacity - Avoid: Hardcoded
#fffor#000absolute colors
The system automatically provides the following protections, ensuring normal display even without styles:
- ✅ All elements
max-width: 100%(prevents overflow) - ✅ Images auto-center, auto-fit
- ✅ Iframe auto-fits container width
- ✅ Text auto-wraps
- ✅ Links default blue, underline on hover
- ✅ Auto-adapts to dark/light mode
Navigation panel width:
- Mobile: 256px
- Desktop: 300px
All content auto-fits panel width. Recommended:
- Image width no more than 120px
- iframes use percentage width (
width: 100%)
Blog configuration supports hot reload. After modifying config.json, no application restart needed—just refresh the article page.
Custom widgets display below the category list in the article page navigation panel, separated by a divider line.
If not configured or configured as empty string, the custom widget area will not display and won't occupy space.
Custom widgets automatically adapt to dark/light themes:
- Divider line color auto-switches
- Link color auto-adjusts
- Recommend using neutral background colors in configuration for dark mode compatibility
Dark mode friendly configuration example:
{
"ArticleSidebarWidget": "<div style='text-align:center; padding:12px; background:rgba(243, 244, 246, 0.5); border-radius:8px;'><img src='/donate.png' style='width:100px;' /></div>"
}- Custom widgets are configured by administrators, stored only in server-side configuration files
- Frontend retrieves via GraphQL API and injects dynamically
- Content rendered in Shadow DOM, isolated from article content
- Recommend using only trusted content and third-party services
- HTML Escaping: When configuring in JSON, ensure proper escaping of double quotes or use single quotes
- Must Use Inline Styles: External CSS classes won't work in Shadow DOM
- Image Paths: Use relative paths (e.g.,
/images/...) or full URLs - Script Support: Currently mainly supports static HTML, images, links, and iframes
- Content Length: Keep it concise to avoid affecting user experience
- Check if JSON format is correct
- Confirm
ArticleSidebarWidgetfield spelling is correct - Refresh article page (not homepage)
- Check browser console for errors
- Make sure to use inline styles (
style="...") - Don't use external CSS classes
- Check if image path is correct
- Check if fixed width is set (e.g.,
width: 500px) - Use percentage width or rely on default
max-width: 100%
{
"Blog": {
"BlogName": "My Tech Blog",
"Blogger": "John Doe",
"Slogan": "Sharing tech, recording life",
"Domain": "https://example.com",
"AnalyticsScript": "<script async src='...'></script>",
"ArticleSidebarWidget": "<div style='text-align:center; padding:16px; background:rgba(249,250,251,0.5); border-radius:8px;'><h4 style='font-size:14px; font-weight:600; margin:0 0 12px;'>❤️ Support Author</h4><img src='/images/wechat-pay.png' style='width:100px; height:100px; border-radius:4px; margin:0 auto 8px;' /><p style='font-size:12px; opacity:0.7; margin:0;'>If the article helps you<br>Feel free to donate</p></div>"
}
}