About cBlog
Tagline: Confluence blogs, beautifully reimagined.
💡 The Inspiration
Confluence is a powerful collaboration platform used by teams worldwide, but its blog experience felt... dated. While modern blogging platforms like Medium, Dev.to, and LinkedIn offer sleek interfaces with rich interactions—reactions, beautiful typography, and seamless navigation—Confluence blogs remained stuck in a more utilitarian design paradigm.
The question was simple: What if we could bring the best of modern blogging to Confluence?
That's where cBlog was born. The vision was to create a native Forge app that would transform how teams read, discover, and engage with blog posts in their Confluence workspace, without leaving the platform they already use daily.
🎯 The Mission
To create a beautiful, interactive blog reading experience within Confluence that:
- Presents blog posts with modern, magazine-quality typography
- Enables emotional engagement through LinkedIn-style reactions
- Makes discovery effortless with elegant navigation
- Renders rich Confluence content (ADF format) perfectly
- Feels native to both Confluence and modern web expectations
🛠️ How We Built It
Architecture Overview
cBlog is built on Atlassian Forge, leveraging both UI Kit and Custom UI capabilities:
Backend (Resolver)
- Node.js 22.x runtime on ARM64 architecture
- Forge Resolver for API gateway and business logic
- Direct integration with Confluence REST APIs (v2)
- Forge Storage API for persistent reaction data
Frontend (Custom UI)
- React 18 with modern hooks architecture
- Tailwind CSS for responsive, utility-first styling
- Custom ADF (Atlassian Document Format) renderer
- Real-time interaction with backend via Forge Bridge
Key Technical Components
1. Blog Discovery Engine
// Pagination-aware fetching to retrieve ALL blog posts
const getAllBlogPosts = async () => {
let allResults = [];
let cursor = null;
do {
const response = await api.requestConfluence(
route`/wiki/api/v2/blogposts?limit=250${cursor ? `&cursor=${cursor}` : ''}`
);
const data = await response.json();
allResults.push(...data.results);
cursor = data._links?.next ? extractCursor(data._links.next) : null;
} while (cursor);
return allResults;
};
2. ADF Content Renderer
Built a comprehensive renderer supporting 25+ ADF node types:
- Rich text (bold, italic, code, underline, strike)
- Structural elements (headings, paragraphs, lists)
- Media (images, embeds with Confluence URL resolution)
- Advanced components (tables, panels, code blocks)
- Inline elements (mentions, emojis, dates, status badges)
3. Reaction System
Implemented a LinkedIn-inspired reaction mechanism:
- 6 reaction types: Like 👍, Love ❤️, Insightful 💡, Celebrate 🎉, Support 🤝, Funny 😂
- User-specific reaction tracking (one reaction per user per post)
- Toggle behavior (click again to remove)
- Real-time count aggregation
- Persistent storage using Forge Storage API
// Reaction data structure
{
counts: { like: 15, love: 8, insightful: 12 },
userReactions: { 'user-123': 'like', 'user-456': 'love' }
}
4. Modern UI/UX
- Gradient headers with decorative elements
- Card-based layouts with subtle shadows and hover effects
- Smooth transitions and animations
- Responsive design (mobile, tablet, desktop)
- Accessible color contrast and semantic HTML
📚 What We Learned
Technical Insights
Forge Custom UI vs UI Kit Trade-offs
- UI Kit is limited to Forge-specific components (no standard React components)
- Custom UI offers full React flexibility but requires separate build pipeline
- Static file serving requires careful path configuration
Confluence API Complexity
- API v2 uses cursor-based pagination (not page numbers)
- Media URLs require complex resolution (contentId, mediaId, collection)
- Author information requires separate user API calls (caching essential)
ADF is Powerful but Challenging
- Deeply nested JSON structure requires recursive rendering
- Many edge cases (empty paragraphs, newlines in text, image fallbacks)
- Different node types have inconsistent attribute patterns
Storage Patterns
- Forge Storage is key-value based (no SQL-like queries)
- Denormalization necessary (store both counts and user mappings)
- Atomic updates important for concurrent reactions
Development Lessons
- Start Simple, Iterate: Built basic list view first, then added detail view, then reactions
- User Experience First: Prioritized polish over feature count
- Progressive Enhancement: App works with degraded experiences if APIs fail
- Defensive Programming: Extensive error handling and fallbacks throughout
🚧 Challenges We Faced
1. Image Rendering Mystery
Problem: Confluence media nodes contain IDs but not direct URLs
Journey:
- Initial attempts: Tried direct media API endpoints → 404s
- Discovered: Need contentId from collection attribute
- Attempted: Multiple URL patterns (
/download/attachments/,/wiki/download/...) - Solution: Implemented fallback chain with
onErrorretry logic
2. User Information Enrichment
Problem: Blog API returns only authorId (UUID), not names/pictures
Solution:
- Built user detail cache to avoid repeated API calls
- Implemented Promise.all for parallel user fetching
- Added graceful degradation (show UUID prefix if API fails)
- Profile picture fallback to generated avatar circles
3. Pagination Cursor Extraction
Problem: Confluence API v2 returns cursor as URL parameter, not directly
Challenge:
// API returns: "_links": { "next": "/wiki/api/v2/blogposts?cursor=abc123..." }
// Need to extract: "abc123..."
Solution:
const extractCursor = (nextUrl) => {
const url = new URL(nextUrl, 'https://dummy.com');
return url.searchParams.get('cursor');
};
4. React in Forge Constraints
Problem: UI Kit doesn't support standard React components
Learning Curve:
- Can't use
<div>,<span>,<strong>→ Must use UI Kit equivalents - Decided to use Custom UI for blog viewer (full React freedom)
- Required setting up separate build pipeline with react-scripts
5. Real-time State Management
Problem: Reactions need to update immediately without page refresh
Implementation:
- Optimistic UI updates (update state before backend confirms)
- Loading states to prevent double-clicks
- Error recovery (revert state if backend fails)
- Smooth animations for visual feedback
6. Deployment & Testing Workflow
Challenge: Forge requires deploy → install cycle for testing
Workflow Developed:
# Development
forge tunnel # Hot reload for code changes
# Manifest changes
forge deploy --non-interactive
forge install --upgrade --non-interactive
# Production
forge deploy --environment production
🎨 Design Philosophy
- Content First: Typography and readability are paramount
- Familiar Yet Fresh: Use known patterns (like LinkedIn reactions) but make them feel native
- Progressive Disclosure: Show essentials first, details on demand
- Performance Matters: Lazy loading, caching, and optimized rendering
- Fail Gracefully: Every API call has a fallback plan
🚀 Future Vision
While cBlog v1 focuses on reading and reacting, the roadmap includes:
- Comments System: Threaded discussions on blog posts
- Search & Filters: Find posts by author, space, date, or reactions
- Bookmarking: Save favorite posts for later
- Analytics Dashboard: Insights on most-read and most-reacted posts
- Author Tools: Publishing interface within the app
- Notifications: Get alerted when posts you care about get updates
🏆 Impact
cBlog demonstrates that enterprise tools don't have to sacrifice user experience. By bringing consumer-grade polish to Confluence, we're showing that:
- Teams can have delightful internal tools
- Modern web patterns work in enterprise contexts
- Forge enables rapid innovation on Atlassian platforms
- Small improvements in UX create outsized engagement gains
The average employee spends hours per week in Confluence. If we can make that time even 10% more enjoyable and productive, we've created real value.
🙏 Acknowledgments
Built with love for the Confluence and Forge communities. Special thanks to:
- The Atlassian Forge team for an incredible platform
- The React community for excellent tooling
- Every developer who's shared Forge insights and patterns
- Teams everywhere who deserve better tools
Tech Stack: Forge | React | Node.js | Confluence API | Tailwind CSS
Status: Production-ready ✨
License: Built for the Forge community
Log in or sign up for Devpost to join the conversation.