A React wrapper component for the Beefree SDK, making it easy to integrate the Beefree email/page builder into your React applications.
- What is Beefree SDK?
- Overview
- Compatibility
- Installation
- Quick Start
- API Reference
- Hooks
- Best Practices
- Advanced Usage
- Examples
- FAQ
- Development
- Other Frameworks
- License
Beefree SDK is a drag-and-drop visual content builder that lets your users design professional emails, landing pages, and popups — without writing code. It powers thousands of SaaS applications worldwide, offering a white-label, embeddable editing experience with real-time collaborative editing, responsive design output, and extensive customization options.
This React package provides a Builder component and a useBuilder hook that handle SDK initialization, lifecycle management, and configuration updates — giving you a React-friendly API to integrate the full Beefree editing experience into your application.
- 🎯 Simple React Integration - Drop-in component with minimal setup
- 🪝 React Hooks Support -
useBuilderhook for programmatic control - 🔄 Dynamic Configuration - Update builder configuration on the fly
- 👥 Collaborative Editing - Support for shared/collaborative sessions
- 📦 TypeScript Support - Full TypeScript definitions included
- 🎨 Customizable - Full access to Beefree SDK configuration options
| Requirement | Version |
|---|---|
| React | 17, 18, or 19 |
| Node.js | >= 18.0.0 |
| TypeScript | >= 4.7 (optional, but recommended) |
| Browsers | Chrome, Firefox, Safari, Edge (latest 2 versions) |
npm install @beefree.io/react-email-builder
# or
yarn add @beefree.io/react-email-builderSign up at developers.beefree.io to get your client_id and client_secret.
Your backend server should exchange credentials for a short-lived token (see Security: Server-Side Token Generation below for details):
// Example: Node.js/Express backend endpoint
app.post('/api/beefree/token', async (req, res) => {
const response = await fetch('https://auth.getbee.io/loginV2', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
client_id: process.env.BEEFREE_CLIENT_ID,
client_secret: process.env.BEEFREE_CLIENT_SECRET,
grant_type: 'password',
}),
})
res.json(await response.json())
})import { useState, useEffect } from 'react'
import { Builder, IToken, useBuilder } from '@beefree.io/react-email-builder'
function EmailEditor() {
const [token, setToken] = useState<IToken | null>(null)
const { save, preview } = useBuilder({
uid: 'user-123',
container: 'bee-container',
language: 'en-US',
})
useEffect(() => {
fetch('/api/beefree/token', { method: 'POST' })
.then((res) => res.json())
.then(setToken)
}, [])
if (!token) return <div>Loading builder...</div>
return (
<div>
<div>
<button onClick={() => preview()}>Preview</button>
<button onClick={() => save()}>Save</button>
</div>
<Builder
id="bee-container"
token={token}
template={{ comments: {}, page: {} }}
onSave={(json, html) => console.log('Saved:', { json, html })}
onError={(err) => console.error('Builder error:', err)}
/>
</div>
)
}| Prop | Type | Required | Description |
|---|---|---|---|
id |
string |
Yes | The container ID (must match the container from useBuilder) |
token |
IToken |
Yes | Authentication token from Beefree API |
template |
IEntityContentJson |
Yes | Initial template/content to load |
shared |
boolean |
No | Enable collaborative editing session |
sessionId |
string |
No | Session ID to join (for collaborative editing) |
width |
React.CSSProperties['width'] |
No | Container width (default: '100%') |
height |
React.CSSProperties['height'] |
No | Container height (default: '800px') |
loaderUrl |
string |
No | Custom loader URL for the Beefree SDK |
onLoad |
() => void |
No | Callback when builder is fully loaded and ready |
onSave |
(json, html) => void |
No | Callback when content is saved |
onChange |
(json, metadata) => void |
No | Callback when content changes |
onError |
(error: BeePluginError) => void |
No | Error callback handler |
onWarning |
(warning: BeePluginError) => void |
No | Warning callback handler |
onSaveRow |
(row: string) => void |
No | Callback when a row is saved |
onRemoteChange |
(data) => void |
No | Callback for collaborative editing remote changes |
onSessionStarted |
(data: { sessionId: string }) => void |
No | Callback when collaborative session starts |
onTemplateLanguageChange |
(language) => void |
No | Callback when template language changes |
useBuilder({
uid: 'user-123',
container: 'bee-container',
// Customization
language: 'en-US',
specialLinks: [
{ type: 'unsubscribe', label: 'Unsubscribe', link: '[unsubscribe]' }
],
// Content management
contentDialog: {
saveRow: {
label: 'Save Row',
handler: (resolve) => resolve({ name: 'My Row' })
}
}
})
return (
<Builder
token={token}
template={{
comments: {},
page: {},
}}
onSave={(jsonFile, htmlFile) => {
console.log('Saved:', {jsonFile, htmlFile})
}}
/>
)The useBuilder hook provides programmatic control over the builder instance and allows you to dynamically update configuration properties (non-callback properties like uid, language, etc.).
import { Builder, useBuilder } from '@beefree.io/react-email-builder'
function MyComponent() {
// Initialize useBuilder with config including container ID
const initialConfig = {
container: 'bee-editor', // This is the ID that links hook and component
uid: 'user-123',
language: 'en-US',
// ...more configs
}
const { updateConfig, load, save, saveAsTemplate } = useBuilder(initialConfig)
// Update configuration dynamically
const changeLanguage = (lang: string) => {
updateConfig({ language: lang })
}
const changeUser = (userId: string) => {
updateConfig({ uid: userId })
}
const handleSave = async () => {
const result = await save()
console.log('Saved:', result)
}
return (
<div>
<button onClick={() => changeLanguage('it-IT')}>Switch to Italian</button>
<button onClick={() => changeUser('user-456')}>Change User</button>
<button onClick={handleSave}>Save</button>
<Builder
id="bee-editor"
token={token}
template={template}
// Define callbacks directly in the component
onSave={(json, html) => {
console.log('Content saved:', json, html)
}}
onChange={(json, metadata) => {
console.log('Content changed')
}}
onLoad={() => {
console.log('Builder is ready!')
}}
/>
</div>
)
}❌ Bad (Insecure):
// DON'T DO THIS!
const token = await fetch('https://auth.beefree.io/token', {
method: 'POST',
body: JSON.stringify({
client_id: 'your-client-id', // ❌ Exposed!
client_secret: 'your-secret', // ❌ Exposed!
})
})✅ Good (Secure):
- Backend API endpoint (Node.js/Express example):
// backend/routes/auth.js
app.post('/api/beefree/token', async (req, res) => {
const response = await fetch('https://auth.beefree.io/token', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
client_id: process.env.BEEFREE_CLIENT_ID,
client_secret: process.env.BEEFREE_CLIENT_SECRET,
uid: req.user.id // Your user's ID
})
})
const token = await response.json()
res.json(token)
})- Frontend:
// frontend/App.tsx
const fetchToken = async () => {
const response = await fetch('/api/beefree/token', {
credentials: 'include' // Include session cookies
})
return response.json()
}Keep your configuration in useMemo to prevent unnecessary re-renders:
const config = useMemo(() => ({
uid: user.id,
container: 'bee-container',
onSave: handleSave,
// ... other config
}), [user.id, handleSave])When using multiple builders on the same page, ensure unique container IDs:
const config1 = useBuilder({ container: 'builder-1', ...otherProperties })
const config2 = useBuilder({ container: 'builder-2', ...otherProperties})For collaborative sessions, share the sessionId between users:
function CollaborativeEditor() {
const [sessionId, setSessionId] = useState<string | null>(null)
return (
<>
{/* Host creates the session */}
<Builder
shared={true}
onSessionStarted={({ sessionId }) => setSessionId(sessionId)}
{...otherProps}
/>
{/* Guest joins with sessionId */}
{sessionId && (
<Builder
shared={true}
sessionId={sessionId}
{...otherProps}
/>
)}
</>
)
}const config = {
contentDialog: {
saveRow: {
label: 'Save to Library',
handler: async (resolve) => {
// Show your custom UI, then resolve
const rowName = await showCustomDialog()
resolve({ name: rowName })
}
},
addOn: {
handler: async (resolve) => {
const content = await fetchCustomContent()
resolve(content)
}
}
}
}const config = {
rowsConfiguration: {
externalContentURLs: [
{
name: 'My Saved Rows',
handle: 'saved-rows',
isLocal: true
}
]
},
hooks: {
getRows: {
handler: async (resolve, reject, args) => {
if (args.handle === 'saved-rows') {
const rows = await fetchSavedRows()
resolve(rows)
} else {
reject('Handle not found')
}
}
}
}
}const config = {
hooks: {
getMentions: {
handler: async (resolve) => {
const mentions = [
{ username: 'FirstName', value: '{{firstName}}', uid: 'fn' },
{ username: 'LastName', value: '{{lastName}}', uid: 'ln' }
]
resolve(mentions)
}
}
}
}The /example directory contains a fully working application that demonstrates:
- Token authentication flow
- Collaborative editing with template preservation
- Save, preview, and export functionality
- Multi-language UI switching
- Multiple builder types (Email, Page, Popup, File Manager)
See the example README for setup instructions.
Quick start:
cp .env.sample .env # Fill in your Beefree credentials
yarn install
yarn start # Opens at http://localhost:3000Authentication requires a client_id and client_secret, which you get by signing up at developers.beefree.io. These credentials should never be exposed in frontend code. Instead, create a backend endpoint that exchanges them for a short-lived token and pass that token to the Builder component. See Security: Server-Side Token Generation for a complete example.
Yes. This package works with any React-based framework. The Builder component renders a client-side iframe, so in Next.js you should use it inside a 'use client' component. For Vite, it works out of the box. For Remix, use a ClientOnly wrapper or lazy-load the component.
Yes. Set shared={true} on the Builder component to create a collaborative session. The onSessionStarted callback provides a sessionId that other users can use to join the same session. See Collaborative Editing for a full example.
The Beefree SDK generates responsive HTML that is compatible with all major email clients, including Gmail, Outlook (desktop and web), Apple Mail, Yahoo Mail, and mobile email apps. The output follows email HTML best practices with inline CSS and table-based layouts for maximum compatibility.
Yes. The Beefree SDK supports extensive UI customization including custom content dialogs, external content sources, merge tags, special links, and more. See Advanced Usage and the Beefree SDK Documentation for the full range of customization options.
Pass your template JSON to the template prop of the Builder component. You can also use the load method from the useBuilder hook to programmatically load a template at any time after initialization.
# Install dependencies
yarn install
# Start development server
yarn start
# Run tests
yarn testyarn buildOutputs:
dist/index.js- CommonJS bundledist/index.es.js- ES module bundledist/index.d.ts- TypeScript definitions
- Verify token is valid and not expired
- Check console for errors
- Ensure
containerID is unique on the page
Beefree SDK wrappers are available for the following frameworks:
| Framework | Package | Repository |
|---|---|---|
| Angular | @beefree.io/angular-email-builder |
BeefreeSDK/angular-email-builder |
For issues related to:
- This React wrapper: Open an issue on this repository
- Beefree SDK: Visit Beefree Developer Documentation
- Account/billing: Contact Beefree Support
