Securely embed the Fraczled editor.
A complete setup guide covering server-gated licensing, SDK configuration, store APIs, and every customizable UI surface.
What you need
Your unique license key (dev or prod).
Identifier for your dashboard project.
A div with a defined height.
Quick Start (Server-Gated)
Validate licenses on your server and return a signed SDK URL.
app.post("/api/editor/init", async (req, res) => {
const { licenseKey, projectId, origin } = req.body;
const response = await fetch("https://fraczled.com/api/license/validate", {
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": "Bearer " + licenseKey
},
body: JSON.stringify({ projectId, origin })
});
const data = await response.json().catch(() => null);
const status = data && typeof data === "object" ? (data.status ?? data) : null;
if (!response.ok || !status || status.isValid === false) {
return res.status(403).json({ error: "license_invalid" });
}
const token = signSdkSession({ origin, projectId });
res.json({ sdkUrl: "/api/editor/sdk?token=" + token });
});
Tip: keep license keys on the server and return only the short-lived sdkUrl to the client.
Client Bootstrap
Request the SDK URL, then initialize the editor after it loads.
<div id="fraczled-editor" style="height: 100vh"></div>
<script>
async function loadEditor() {
const response = await fetch("/api/editor/init", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
licenseKey: "prod_...",
projectId: "proj_123",
origin: window.location.origin
})
});
if (!response.ok) {
const error = await response.json().catch(() => ({}));
throw new Error(error.message || "License validation failed");
}
const { sdkUrl } = await response.json();
const script = document.createElement("script");
script.src = sdkUrl;
script.defer = true;
document.head.appendChild(script);
script.onload = () => {
FraczledSDK.createFraczledEditor({
container: document.getElementById("fraczled-editor"),
apiKey: "prod_...",
projectId: "proj_123"
});
};
}
loadEditor().catch((err) => {
console.error(err);
alert("Failed to load editor. Check the console for details.");
});
</script>
Secure Setup (Required)
The editor must be served through your backend. Your server validates the license and only then returns a signed SDK URL.
1) Validate on your server
- Send projectId + origin to /api/license/validate.
- Reject if isValid is false.
- Never expose raw keys to the browser in production.
2) Issue short-lived access
- Return a signed token (5 minutes max).
- Serve the SDK bundle only when the token is valid.
- Invalidate tokens when licenses are revoked.
3) Initialize the editor
- Fetch /api/editor/init from the browser.
- Load the SDK URL your server returns.
- Call createFraczledEditor.
Security rules (non-negotiable)
- Always gate the SDK bundle through your backend.
- Validate projectId + origin on every init request.
- Do not cache or reuse SDK tokens beyond their TTL.
- Offline usage is only allowed with a signed offline token issued by Fraczled.
Configuration Reference
Every configuration option supported by createFraczledEditor.
createFraczledEditor({
apiKey: "YOUR_LICENSE_KEY",
projectId: "YOUR_PROJECT_ID",
container: document.getElementById("editor"),
showCredit: true,
initialState: { pages: [], settings: {} },
services: {
licenseRequest: {
projectId: "YOUR_PROJECT_ID",
customerId: "cus_123",
offlineLicense: OFFLINE_TOKEN
},
licenseTimeoutMs: 2500,
aiEndpoint: "https://your-api.example.com/ai",
unsplashProxyBaseUrl: "https://your-api.example.com/unsplash"
},
assets: {
injectTailwind: true,
tailwindCdnUrl: "https://cdn.tailwindcss.com",
injectFont: true,
fontHref: "https://fonts.googleapis.com/css2?family=Inter:wght@400;600&display=swap",
injectBaseStyles: true,
showLoader: true
},
ui: {
hideDefaultPanels: false,
hiddenPanels: [],
topbarMode: "default",
newDesignModal: {
render: (ctx) => null,
sizePresets: {
social: [{ name: "Square", width: 1080, height: 1080, unit: "px", dpi: 72 }],
print: [{ name: "Postcard", width: 6, height: 4, unit: "in", dpi: 300, bleed: 0.125 }]
}
}
},
customPanels: [],
customContextMenuItems: [],
customToolbarItems: [],
keyboardShortcuts: {},
customFonts: {},
customTemplates: {},
customExportFormats: [],
customElementTypes: [],
theme: {}
});
| Key | Required | Purpose |
|---|---|---|
| apiKey | Yes | License key for validation. |
| projectId | Yes | Must match the project set in your dashboard. |
| container | Yes | DOM element the editor renders into. |
| showCredit | No | Show or hide the watermark (license can override). |
| initialState | No | Provide pages + settings to preload a design. |
| services | No | License request payload, timeout, and AI/Unsplash endpoints. |
| assets | No | Control injected Tailwind + fonts + loader. |
| ui | No | Hide panels, custom topbar, new design modal, size presets. |
| customPanels | No | Add sidebar tabs and panels. |
| customToolbarItems | No | Add topbar and object toolbar actions. |
| customContextMenuItems | No | Extend right-click menus on canvas or elements. |
| customTemplates | No | Provide templates, categories, and template save action. |
| customExportFormats | No | Register export formats in the Export menu. |
| customElementTypes | No | Add your own element types and renderers. |
| customFonts | No | Define font lists or custom loader. |
| keyboardShortcuts | No | Override or add keyboard shortcuts. |
| theme | No | Customize editor colors and layout styling. |
Offline tokens are issued by Fraczled and are required for offline mode. If you need one, contact support.
Store API
The store is the single source of truth for the editor. Use it to read state, mutate content, and listen for changes.
const editor = createFraczledEditor({ /* config */ });
const { store } = editor;
// Read state
console.log(store.state.pages, store.state.settings);
// Subscribe to changes
const unsubscribe = store.subscribe(() => {
console.log("State updated", store.state);
});
// Mutate
store.addElement({ type: "text", content: "Hello", width: 240, height: 80 });
store.updateElement(store.state.selection[0], { color: "#0f172a" });
// Dirty state
if (store.hasUnsavedChanges()) {
console.log("Unsaved changes detected");
}
// Persist
const json = store.exportJSON();
store.loadJSON(json);
// Cleanup
unsubscribe();
Read + Subscribe
- store.state gives the latest snapshot.
- store.subscribe() fires on any state change.
- store.subscribeLicense() fires when entitlements update.
- store.hasUnsavedChanges() exposes dirty state.
License + Entitlements
- store.getLicenseStatus() returns plan + state.
- store.getEntitlements() returns feature access.
- Use these values to gate your own UI.
| Category | Methods |
|---|---|
| Pages | addPage, duplicatePage, deletePage, renamePage, setActivePage |
| Elements | addElement, updateElement, deleteElement, duplicateElement |
| Selection | setSelection, selectAll, deselectAll, deleteSelection |
| Guides | addGuide, updateGuide, deleteGuide, clearGuides |
| Settings | updateSettings |
| Templates | loadTemplateAsNewDesign |
| Dirty State | hasUnsavedChanges, markClean |
| History | undo, redo, canUndo, canRedo |
| Persistence | exportJSON, loadJSON, reset |
Store Items & UI Extensions
Everything you can replace or extend in the SDK. These are the official extension points and are fully supported.
| Store Item | Config Key | Plan Feature | Purpose |
|---|---|---|---|
| Custom Topbar Actions | ui.topbarMode + customToolbarItems | topbarActions | Replace save/new/export/undo/redo UI. |
| New Design Modal | ui.newDesignModal.render | newDesignModal | Replace the Create New Design modal UI. |
| Template Size Presets | ui.newDesignModal.sizePresets | templateSizes | Override social/print sizes and bleeds. |
| Save as Template Button | customTemplates.saveAction | templateSave | Customize "Save Current Design as Template". |
| Sidebar Tabs + Panels | customPanels, ui.hiddenPanels | (optional via feature) | Add your own tools and workflows. |
| Context Menu Items | customContextMenuItems | None | Extend right-click menus. |
| Toolbar Items | customToolbarItems | None | Add buttons in object toolbar or topbar. |
| Custom Export Formats | customExportFormats | None | Add new export actions. |
| Custom Element Types | customElementTypes | None | Render your own element types. |
| Custom Fonts | customFonts | None | Supply fonts or a font loader. |
| Custom Templates | customTemplates | templates | Provide template data or fetch templates. |
| Keyboard Shortcuts | keyboardShortcuts | None | Override defaults or add custom combos. |
| Theme | theme | None | Customize editor colors and layout. |
Custom Topbar Example
createFraczledEditor({
ui: { topbarMode: "custom" },
customToolbarItems: [
{
id: "my-topbar-export",
label: "Export",
location: "topbar",
onClick: ({ topbar }) => topbar?.onExport?.("web")
}
]
});
Template Save Action
createFraczledEditor({
customTemplates: {
saveAction: {
label: "Save as Brand Template",
onClick: ({ openSaveModal }) => openSaveModal()
}
}
});
Plan-gated features
Plan features are configured in https://www.fraczled.com/admin/ and enforced by the license response.
- topbarActions - Custom topbar UI
- templateSave - Save as Template action
- newDesignModal - Custom Create New Design modal
- templateSizes - Custom size presets
- templates, design, elements, draw
- text, images, brand, uploads
- qrcode, layers, saveLoad
Templates & Size Presets
Provide your own templates and define the exact sizes + bleeds your product requires.
Custom templates
Templates can be static, fetched from your API, or merged with Fraczled templates.
createFraczledEditor({
customTemplates: {
templates: [
{ id: "t1", name: "Promo", category: "marketing", tags: ["promo"], thumbnail: "...", designState: {} }
],
fetchTemplates: async () => fetch("/api/templates").then(r => r.json()),
categories: ["marketing", "product"],
hideDefaultTemplates: false
}
});
Size presets with bleeds
Use inches, millimeters, points, or pixels. Bleed/safe area uses the same unit.
createFraczledEditor({
ui: {
newDesignModal: {
sizePresets: {
social: [
{ name: "Square", width: 1080, height: 1080, unit: "px", dpi: 72 }
],
print: [
{ name: "Sleeve", width: 8.25, height: 5.5, unit: "in", dpi: 300, bleed: 0.125 }
]
}
}
}
});
Unit support
Accepted units: px, in, mm, pt. The editor converts units automatically.
Guides, Rulers & Snapping
Ruler guides are persistent, per-page alignment targets that share the same Smart Guide engine as object snapping.
Persistent ruler guides
- Guides live on each page (per-artboard).
- Locking prevents edits but still allows snapping.
- Hidden guides only snap when snapToHiddenGuides is true.
Smart guides & snapping
- Object-to-object and object-to-guide use the same alignment rules.
- Snapping can be toggled without disabling visual feedback.
- On touch devices, snapping uses a slightly higher tolerance.
Guide + snap settings
store.updateSettings({
showRulers: true,
showGuides: true,
lockGuides: false,
showSmartGuides: true,
snapEnabled: true,
snapToObjects: true,
snapToGuides: true,
snapToHiddenGuides: false
});
Guide API
const pageId = store.state.activePageId;
store.addGuide(pageId, { axis: "x", position: 120 });
store.addGuide(pageId, { axis: "y", position: 320 });
store.updateGuide(pageId, guideId, { position: 240 });
store.deleteGuide(pageId, guideId);
store.clearGuides(pageId);
Guide coordinate system
Guide positions are stored in canvas pixels. If you work in inches/mm/pt, convert with your unit helpers before calling guide methods.
Events & Saving
Listen to editor events and persist designs as JSON.
editor.on("change", (state) => {
console.log("State updated", state);
});
editor.on("save", (payload) => {
console.log("User saved", payload);
});
editor.on("export", (payload) => {
console.log("Exported", payload);
});
// Persist
const json = editor.save();
await fetch("/api/designs", { method: "POST", body: json });
Event types
- change - any editor update
- save - user saved a design
- select - selection changed
- export - export completed
- designSaved - saved design created/updated
- designDeleted - saved design removed
- toast - inline notifications from the SDK
Best practices
- Store JSON in your DB; reload via initialState or loadJSON.
- Debounce autosave on change events.
- Always keep the editor container height set.
Licensing & Security
Licenses are validated server-side. The editor remains restricted until validation succeeds.
Server-gated delivery
Your backend must validate the license and return a signed SDK URL. This is required for production use.
License states
- pending - restricted while validating
- valid - full access per entitlements
- invalid - blocked + watermark
- offline - allowed only with signed token
Offline mode
Offline usage requires a signed offline license token issued by Fraczled. No token means no offline access.
{
"isValid": true,
"state": "valid",
"plan": "team",
"showWatermark": false,
"entitlements": {
"ai": true,
"export": { "web": true, "print": true },
"features": {
"templates": true,
"topbarActions": true,
"templateSave": true,
"newDesignModal": true,
"templateSizes": true
},
"limits": { "maxPages": 50 }
}
}
Production rule: the SDK bundle must only be served after your server validates the license and issues a short-lived token.
Deploy Checklist
Use this checklist before pushing to production.
Licensing
Always server-gate the SDK. Validate projectId + origin, issue short-lived tokens, and revoke when needed.
Layout
Give the editor a fixed height or full viewport. Call destroy() on teardown.
Assets
Disable injected Tailwind if it conflicts with your app. Prefer self-hosted fonts in production.
Support
Need help integrating?
Our engineering team can help you debug integration issues or plan custom SDK workflows.