How to Build an Automated Facebook Posting System with React and Netlify
The Problem
My client had 10 social media ad designs built as React components. They wanted to post one per day to Facebook without:
- Manually posting every day
- Paying for Buffer, Hootsuite, or similar tools
- Exporting images separately and uploading them
The solution? Build it into the existing React app with Netlify scheduled functions.
The Architecture
Here's what we built:
React Component (Design)
↓
html-to-image (Capture at 2048x2048)
↓
Base64 PNG stored in PostgreSQL
↓
Netlify Scheduled Function (runs daily at 9 AM)
↓
Facebook Graph API (posts the image)
Step 1: Capturing React Components as Images
The first challenge was converting React components to high-quality images. I used the html-to-image library.
The Key Insight: Capture the Right Element
My first attempts produced images with white bars or shadows. The fix was simple but crucial:
// WRONG - captures shadows and decorative CSS
const dataUrl = await toPng(cardContainer, { ... });
// CORRECT - capture only the content wrapper
const contentWrapper = target.querySelector('[data-content-wrapper]');
const dataUrl = await toPng(contentWrapper, { ... });
The Clone Technique for Crisp Text
For Facebook's 2048px requirement, I clone the element at full size before capturing:
// Clone at 2048px for crisp text rendering
const clone = contentWrapper.cloneNode(true) as HTMLElement;
clone.style.position = 'fixed';
clone.style.left = '-9999px';
clone.style.width = '2048px';
clone.style.height = '2048px';
clone.style.transform = 'none';
document.body.appendChild(clone);
// Capture at 1:1 ratio (element is already at target size)
const base64 = await toPng(clone, {
quality: 1,
pixelRatio: 1,
width: 2048,
height: 2048,
});
document.body.removeChild(clone);
Step 2: Database Schema for Scheduled Posts
I added a simple table to store scheduled posts:
CREATE TABLE scheduled_posts (
id SERIAL PRIMARY KEY,
design_id INTEGER NOT NULL,
caption TEXT NOT NULL,
image_base64 TEXT NOT NULL,
scheduled_for TIMESTAMP NOT NULL,
status TEXT DEFAULT 'pending',
facebook_post_id TEXT,
error_message TEXT,
created_at TIMESTAMP DEFAULT NOW(),
posted_at TIMESTAMP
);
The image_base64 field stores the pre-captured image so the scheduled function doesn't need to render React components.
Step 3: The Scheduling UI
I added a "Schedule 10 Days of Posts" button that:
- Loops through all 10 designs
- Switches to each design tab (to render it)
- Captures at 2048x2048
- Saves to database with staggered dates
const scheduleAllPosts = async () => {
for (let i = 0; i < designs.length; i++) {
const design = designs[i];
// Switch to this design to render it
setActiveDesign(design.id);
await new Promise(resolve => setTimeout(resolve, 500));
// Capture the design
const base64 = await captureDesign();
// Schedule for tomorrow + i days at 9 AM
const scheduledDate = new Date();
scheduledDate.setDate(scheduledDate.getDate() + 1 + i);
scheduledDate.setHours(9, 0, 0, 0);
await apiRequest("POST", "/api/scheduled-posts", {
designId: design.id,
caption: generateCaption(design),
imageBase64: base64,
scheduledFor: scheduledDate.toISOString(),
});
}
};
Step 4: Netlify Scheduled Function
The magic happens in a Netlify scheduled function that runs daily:
// netlify/functions/scheduled-facebook-post.ts
import type { Config } from "@netlify/functions";
export default async function handler() {
// Find pending posts due now
const pendingPosts = await sql`
SELECT * FROM scheduled_posts
WHERE status = 'pending'
AND scheduled_for <= NOW()
LIMIT 1
`;
if (pendingPosts.length === 0) return;
const post = pendingPosts[0];
// Post to Facebook
const formData = new FormData();
formData.append('source', Buffer.from(post.image_base64, 'base64'));
formData.append('message', post.caption);
formData.append('access_token', process.env.FACEBOOK_ACCESS_TOKEN);
const response = await axios.post(
`https://graph.facebook.com/v24.0/${pageId}/photos`,
formData
);
// Mark as posted
await sql`
UPDATE scheduled_posts
SET status = 'posted', facebook_post_id = ${response.data.id}
WHERE id = ${post.id}
`;
}
// Run daily at 10 PM UTC = 9 AM Sydney (AEDT)
export const config: Config = {
schedule: "0 22 * * *"
};
Step 5: Facebook Access Token (The Tricky Part)
Facebook tokens expire. Here's how to get a long-lived one:
Get a Short-Lived Token
- Go to Graph API Explorer
- Select your app
- Add permissions:
pages_show_list,pages_manage_posts - Generate token
Exchange for Long-Lived Token (60 days)
GET https://graph.facebook.com/v24.0/oauth/access_token?
grant_type=fb_exchange_token&
client_id={APP_ID}&
client_secret={APP_SECRET}&
fb_exchange_token={SHORT_LIVED_TOKEN}
Get Page Access Token (Never Expires)
GET https://graph.facebook.com/v24.0/me/accounts?
access_token={LONG_LIVED_USER_TOKEN}
The Management UI
I also built a simple UI to view and manage scheduled posts:
- See all pending, posted, and failed posts
- Manual "Post Now" button for testing
- Delete button to remove scheduled posts
- Status indicators (green checkmark for posted)
Common Issues and Fixes
White Bar at Top of Image
Cause: Capturing outer container with shadow-2xl
Fix: Capture data-content-wrapper directly
Blurry Text on Facebook
Cause: Scaling up small images Fix: Clone element at 2048px, capture at 1:1 ratio
PayloadTooLargeError
Cause: Express body limit too small
Fix: app.use(express.json({ limit: '10mb' }))
Facebook Timeline Looks Blurry
Cause: Normal Facebook behavior - they compress thumbnails Fix: None needed - full-size image is crisp when clicked
The Results
- One-click scheduling for 10 days of posts
- Zero manual posting required
- High-quality images that look crisp on Facebook
- No third-party tools or monthly fees
- Full control over timing and content
Why Build This Instead of Using Buffer?
- Cost: Free vs $15+/month
- Control: Custom capture logic for React components
- Integration: Lives in the existing codebase
- Learning: Understanding the Facebook API is valuable
- Flexibility: Can extend to Instagram, Twitter, etc.
The Tech Stack
- Frontend: React + TypeScript + TailwindCSS
- Image Capture: html-to-image
- Database: PostgreSQL (Neon)
- Hosting: Netlify (with scheduled functions)
- API: Facebook Graph API v24.0
Want to Build Something Similar?
The key insights:
- Capture the right element - avoid shadows and decorative CSS
- Clone at target size - don't scale up, render at full resolution
- Use 2048px for Facebook - prevents compression
- Store pre-captured images - scheduled functions can't render React
- Get a long-lived token - or you'll be refreshing every hour
This entire system was built in a few hours using AI-assisted development. The hardest part wasn't the code - it was figuring out Facebook's image quality quirks and token system.
Have questions about building automated social media systems? Get in touch - I love solving these kinds of problems.
Visual Summary
Shareable social cards summarising this guide.
Social Media Carousel
8 cards • Download as ZIP (images) or PDF (LinkedIn)
Automate Facebook Posting
React + Netlify System
Build a scheduled posting system that captures React components as images and posts them automatically - no third-party tools.

Facebook supports 720px, 960px, or 2048px. Using 2048px prevents Facebook from resizing and keeps text crisp.

The Architecture
- 1
React component renders the design
- 2
html-to-image captures at 2048x2048
- 3
Base64 PNG stored in PostgreSQL
- 4
Netlify scheduled function posts daily
- 5
Facebook Graph API receives the image

The Clone Technique
Clone the element at full 2048px size before capturing. This ensures text renders crisply instead of being scaled up from a smaller capture.

Capture the Right Element
Wrong: Capture container with shadows
Right: Capture only content wrapper

What You Need
- ✓
React app with design components
- ✓
Netlify account with scheduled functions
- ✓
Facebook Developer App with page access
- ✓
PostgreSQL database for image queue

Why Build It Yourself?
No monthly fees for scheduling tools. Full control over image quality. Integrates with your existing React components.

Need Custom Automation?
We build marketing automation systems that integrate with your tech stack.

Share This Article
Spread the knowledge