Template Builder Web Component
The<docuseal-builder> web component renders the full template editor inside your application. Users can upload documents, drag and drop fields, configure submitter roles, and save templates — all without leaving your page.
Quick start
Generate a JWT token server-side, then embed the builder:<script src="https://your-app.com/js/docutrust-embed.js"></script>
<docuseal-builder
data-token="eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxNSIsImlzcyI6ImRvY3V0cnVzdCIsImF1ZCI6ImJ1aWxkZXIiLCJhaWQiOiIxIiwiaWF0IjoxNzQ0MTk1OTIwLCJleHAiOjE3NDQyMTAzMjAsIm9wdHMiOnsiYXV0b3NhdmUiOnRydWUsInByZXZpZXciOnRydWUsImZpZWxkcyI6W119fQ.abc123"
></docuseal-builder>
The builder always requires authentication. Use a JWT token from
POST /api/embed/token (recommended for embedded use) or pass data-template-id when the user is already authenticated via a session cookie.Attributes
The<docuseal-builder> component accepts 33 data-* attributes to control behavior, authentication, and UI.
| Attribute | Type | Default | Description |
|---|---|---|---|
data-token | string | — | JWT token generated via POST /api/embed/token with audience: "builder". This is the recommended authentication method for embedded builders. The token encodes the template ID, account scope, and UI options. |
data-host | string | current origin | The base URL of your DocuTrust instance. Only needed when the embed script is served from a different domain than the DocuTrust API. Example: "https://docs.your-app.com". |
data-template-id | string | — | Template ID to load in the builder. Only works when the user has an active session cookie (i.e., they are logged into DocuTrust in the same browser). For third-party embedding, use data-token instead. |
data-language | string | — | UI language code. Supported values: "en", "es", "fr", "de", "pt", "it", "nl", "ja", "zh", "ko", "ar", "he". |
data-autosave | "false" | "true" | Set to "false" to disable automatic saving. When disabled, the template is only saved when the user clicks Save or you call builder.save() programmatically. |
data-preview | "false" | "true" | Set to "false" to hide the document preview panel on the right side of the builder. |
data-with-title | "false" | "true" | Set to "false" to hide the editable template title field at the top of the builder. |
data-with-send-button | "false" | "true" | Set to "false" to hide the “Send” button. Useful when you want to handle submission creation through your own UI or API calls. |
data-with-recipients | "false" | "true" | Set to "false" to hide the recipients/submitter roles section. Useful when roles are pre-configured and should not be changed by the user. |
data-only-defined-fields | "true" | — | When set, the field palette only shows the field types listed in data-fields. All other field types are hidden. |
data-fields | JSON string | — | Pre-define the available fields as a JSON array. Each entry specifies a field name, type, and optional role assignment. See the Fields configuration section below. |
data-roles | JSON string | — | Pre-define submitter roles as a JSON array of role name strings. When provided, the user cannot add or remove roles. Example: '["Sender", "Recipient", "Witness"]'. |
data-submitters | JSON string | — | Pre-define default submitters as a JSON array of objects with name and email properties. Example: '[{"name": "Recipient", "email": "jane@example.com"}]'. |
data-required-fields | JSON string | — | A JSON array of field names that must be placed on the document before saving. The builder shows a warning if any required field is missing. Example: '["Signature", "Date"]'. |
data-field-types | string | — | Comma-separated list of allowed field types in the field palette. Only the specified types are available for placement. Example: "text,signature,date,checkbox". |
data-draw-field-type | string | — | Default field type when the user draws a new field on the document by clicking and dragging. Example: "text", "signature". |
data-custom-button-title | string | — | Custom text for the primary action button in the builder toolbar. Replaces the default “Send” label. Example: "Publish Template". |
data-custom-button-url | string | — | URL to navigate to when the custom button is clicked. When set, clicking the button navigates instead of triggering the send event. |
data-with-upload-button | "false" | "true" | Set to "false" to hide the document upload button. Useful when documents are pre-loaded via the API. |
data-with-add-page-button | "true" | — | Set to "true" to show an “Add blank page” button, allowing users to append empty pages to the document. |
data-with-sign-yourself-button | "false" | "true" | Set to "false" to hide the “Sign yourself” button in the builder toolbar. |
data-with-documents-list | "false" | "true" | Set to "false" to hide the documents sidebar listing all uploaded documents. |
data-with-fields-list | "false" | "true" | Set to "false" to hide the fields sidebar listing all placed fields. |
data-with-fields-detection | "true" | — | Set to "true" to show the AI-powered field detection button that automatically identifies and places fields on the document. |
data-with-title-input | "true" | — | Set to "true" to show the editable template title input at the top of the builder. Equivalent to data-with-title. |
data-with-send-myself-email | "true" | — | Set to "true" to show an option for the user to send themselves a copy of the template via email. |
data-email-subject | string | — | Custom email subject line for emails sent from the builder. Supports template tokens like {{template.name}}. |
data-email-body | string | — | Custom email body for emails sent from the builder. Supports template tokens like {{template.name}} and {{submitter.link}}. |
data-background-color | string | — | Background color for the builder as a HEX code (e.g., "#f9fafb"). Overrides the default background. |
data-custom-css | string | — | Custom CSS rules injected into the builder iframe. Allows overriding default styles for advanced branding. Example: "body { font-family: 'Inter', sans-serif; }". |
data-input-mode | "true" | — | When set to "true", enables data prefill mode. The builder shows a form for entering field values instead of the full drag-and-drop editor. Useful for creating pre-filled templates programmatically. |
data-with-signature-id | "true" | — | When set to "true", displays a unique signature identifier alongside each signature field. Useful for audit and compliance tracking. |
data-extract-fields | "false" | "true" | Set to "false" to disable automatic extraction of form fields from uploaded PDF documents. By default, DocuTrust auto-detects and places fields found in interactive PDF forms. |
Full HTML example
<script src="https://your-app.com/js/docutrust-embed.js"></script>
<docuseal-builder
id="template-builder"
data-token="eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxNSIsImlzcyI6ImRvY3V0cnVzdCIsImF1ZCI6ImJ1aWxkZXIiLCJhaWQiOiIxIiwiaWF0IjoxNzQ0MTk1OTIwLCJleHAiOjE3NDQyMTAzMjAsIm9wdHMiOnsiYXV0b3NhdmUiOnRydWUsInByZXZpZXciOnRydWUsImZpZWxkcyI6W119fQ.abc123"
data-host="https://your-app.com"
data-language="en"
data-autosave="true"
data-preview="true"
data-with-title="true"
data-with-title-input="true"
data-with-send-button="true"
data-with-recipients="true"
data-with-upload-button="true"
data-with-add-page-button="false"
data-with-sign-yourself-button="true"
data-with-documents-list="true"
data-with-fields-list="true"
data-with-fields-detection="true"
data-with-send-myself-email="false"
data-only-defined-fields="true"
data-draw-field-type="text"
data-field-types="text,signature,initials,date,checkbox,select"
data-custom-button-title="Publish Template"
data-custom-button-url="https://your-app.com/templates/15/published"
data-email-subject="Please sign: {{template.name}}"
data-email-body="Hi, please review and sign the document: {{submitter.link}}"
data-background-color="#f9fafb"
data-custom-css="body { font-family: 'Inter', sans-serif; }"
data-input-mode="false"
data-with-signature-id="false"
data-extract-fields="true"
data-roles='["Sender", "Recipient", "Witness"]'
data-submitters='[
{"name": "Recipient", "email": "jane@example.com"},
{"name": "Witness", "email": "witness@example.com"}
]'
data-required-fields='["Recipient Signature", "Sender Signature", "Witness Signature"]'
data-fields='[
{"name": "Full Name", "type": "text", "role": "Recipient", "required": true},
{"name": "Email", "type": "text", "role": "Recipient", "required": true},
{"name": "Company", "type": "text", "role": "Recipient", "required": false},
{"name": "Start Date", "type": "date", "role": "Recipient", "required": true},
{"name": "Recipient Signature", "type": "signature", "role": "Recipient", "required": true},
{"name": "Recipient Initials", "type": "initials", "role": "Recipient", "required": true},
{"name": "Sender Signature", "type": "signature", "role": "Sender", "required": true},
{"name": "Witness Signature", "type": "signature", "role": "Witness", "required": true},
{"name": "Agreed to Terms", "type": "checkbox", "role": "Recipient", "required": true},
{"name": "Department", "type": "select", "role": "Recipient", "required": false, "options": ["Engineering", "Sales", "Marketing", "Operations"]}
]'
></docuseal-builder>
Events
The<docuseal-builder> component emits DOM custom events. Listen with addEventListener on the element.
init
Fired when the web component is initialized and the iframe begins loading.document.getElementById('template-builder').addEventListener('init', function (event) {
console.log('Builder iframe is loading');
});
null
load
Fired when the iframe has loaded and the builder is ready to render.document.getElementById('template-builder').addEventListener('load', function (event) {
console.log('Builder iframe loaded');
});
null
loaded
Fired when the builder UI has fully rendered and is interactive.document.getElementById('template-builder').addEventListener('loaded', function (event) {
console.log('Builder ready:', event.detail);
});
{
"template_id": 15,
"template_name": "Employment Agreement",
"account_id": 1,
"submitters": [
{
"name": "Sender",
"uuid": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
},
{
"name": "Recipient",
"uuid": "b2c3d4e5-f6a7-8901-bcde-f12345678901"
},
{
"name": "Witness",
"uuid": "c3d4e5f6-a7b8-9012-cdef-123456789012"
}
],
"documents": [
{
"id": 42,
"filename": "employment_agreement.pdf",
"content_type": "application/pdf",
"page_count": 4
}
],
"fields": [
{
"name": "Full Name",
"type": "text",
"required": true,
"submitter_uuid": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
"page": 1
},
{
"name": "Recipient Signature",
"type": "signature",
"required": true,
"submitter_uuid": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
"page": 4
}
]
}
template-saved
Fired when the template is saved (either by auto-save or the user clicking Save).document.getElementById('template-builder').addEventListener('template-saved', function (event) {
var data = event.detail;
console.log('Template saved:', data.template_id);
console.log('Fields:', data.fields.length);
});
{
"template_id": 15,
"template_name": "Employment Agreement",
"updated_at": "2026-04-08T14:22:45.000Z",
"submitters": [
{
"name": "Sender",
"uuid": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
},
{
"name": "Recipient",
"uuid": "b2c3d4e5-f6a7-8901-bcde-f12345678901"
},
{
"name": "Witness",
"uuid": "c3d4e5f6-a7b8-9012-cdef-123456789012"
}
],
"documents": [
{
"id": 42,
"filename": "employment_agreement.pdf",
"content_type": "application/pdf",
"page_count": 4
}
],
"fields": [
{
"name": "Full Name",
"type": "text",
"required": true,
"submitter_uuid": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
"page": 1,
"areas": [{"x": 0.15, "y": 0.32, "w": 0.35, "h": 0.03, "page": 1}]
},
{
"name": "Email",
"type": "text",
"required": true,
"submitter_uuid": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
"page": 1,
"areas": [{"x": 0.15, "y": 0.38, "w": 0.35, "h": 0.03, "page": 1}]
},
{
"name": "Start Date",
"type": "date",
"required": true,
"submitter_uuid": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
"page": 2,
"areas": [{"x": 0.55, "y": 0.12, "w": 0.20, "h": 0.03, "page": 2}]
},
{
"name": "Recipient Signature",
"type": "signature",
"required": true,
"submitter_uuid": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
"page": 4,
"areas": [{"x": 0.10, "y": 0.72, "w": 0.30, "h": 0.08, "page": 4}]
},
{
"name": "Sender Signature",
"type": "signature",
"required": true,
"submitter_uuid": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"page": 4,
"areas": [{"x": 0.55, "y": 0.72, "w": 0.30, "h": 0.08, "page": 4}]
}
]
}
template-data
Fired when the builder emits template data in response to agetTemplate() call or internal state change.
document.getElementById('template-builder').addEventListener('template-data', function (event) {
console.log('Template data:', event.detail);
});
template-saved.
send
Fired when the user clicks the “Send” button inside the builder.document.getElementById('template-builder').addEventListener('send', function (event) {
var data = event.detail;
console.log('User clicked Send for template:', data.template_id);
// Open your own send dialog or create a submission via the API
});
{
"template_id": 15,
"template_name": "Employment Agreement",
"submitters": [
{
"name": "Sender",
"uuid": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
},
{
"name": "Recipient",
"uuid": "b2c3d4e5-f6a7-8901-bcde-f12345678901"
},
{
"name": "Witness",
"uuid": "c3d4e5f6-a7b8-9012-cdef-123456789012"
}
]
}
upload
Fired when a document is uploaded to the builder.document.getElementById('template-builder').addEventListener('upload', function (event) {
var data = event.detail;
console.log('Document uploaded:', data.filename);
});
{
"template_id": 15,
"document_id": 42,
"filename": "employment_agreement.pdf",
"content_type": "application/pdf",
"size": 184320,
"page_count": 4
}
change
Fired when any change occurs in the builder — field added, moved, resized, deleted, or properties updated. Useful for tracking unsaved changes or implementing custom auto-save logic.document.getElementById('template-builder').addEventListener('change', function (event) {
var data = event.detail;
console.log('Builder changed:', data.change_type);
console.log('Template ID:', data.template_id);
console.log('Fields count:', data.fields.length);
});
{
"template_id": 15,
"template_name": "Employment Agreement",
"change_type": "field_added",
"submitters": [
{
"name": "Sender",
"uuid": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
},
{
"name": "Recipient",
"uuid": "b2c3d4e5-f6a7-8901-bcde-f12345678901"
},
{
"name": "Witness",
"uuid": "c3d4e5f6-a7b8-9012-cdef-123456789012"
}
],
"documents": [
{
"id": 42,
"filename": "employment_agreement.pdf",
"content_type": "application/pdf",
"page_count": 4
}
],
"fields": [
{
"name": "Full Name",
"type": "text",
"required": true,
"submitter_uuid": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
"page": 1,
"areas": [{"x": 0.15, "y": 0.32, "w": 0.35, "h": 0.03, "page": 1}]
},
{
"name": "Recipient Signature",
"type": "signature",
"required": true,
"submitter_uuid": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
"page": 4,
"areas": [{"x": 0.10, "y": 0.72, "w": 0.30, "h": 0.08, "page": 4}]
}
]
}
Public methods
The<docuseal-builder> element exposes two public methods for programmatic interaction.
getTemplate()
Returns a Promise that resolves with the current template state. The method communicates with the builder iframe viapostMessage and has a 5-second timeout.
var builder = document.getElementById('template-builder');
async function saveAndLog() {
try {
var template = await builder.getTemplate();
console.log('Template ID:', template.template_id);
console.log('Template name:', template.template_name);
console.log('Fields:', template.fields.length);
console.log('Submitters:', template.submitters.map(function (s) { return s.name; }));
} catch (error) {
console.error('Failed to get template (timeout or error):', error.message);
}
}
saveAndLog();
{
"template_id": 15,
"template_name": "Employment Agreement",
"updated_at": "2026-04-08T14:22:45.000Z",
"submitters": [
{
"name": "Sender",
"uuid": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
},
{
"name": "Recipient",
"uuid": "b2c3d4e5-f6a7-8901-bcde-f12345678901"
},
{
"name": "Witness",
"uuid": "c3d4e5f6-a7b8-9012-cdef-123456789012"
}
],
"documents": [
{
"id": 42,
"filename": "employment_agreement.pdf",
"content_type": "application/pdf",
"page_count": 4
}
],
"fields": [
{
"name": "Full Name",
"type": "text",
"required": true,
"submitter_uuid": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
"page": 1,
"areas": [{"x": 0.15, "y": 0.32, "w": 0.35, "h": 0.03, "page": 1}]
},
{
"name": "Recipient Signature",
"type": "signature",
"required": true,
"submitter_uuid": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
"page": 4,
"areas": [{"x": 0.10, "y": 0.72, "w": 0.30, "h": 0.08, "page": 4}]
}
]
}
save()
Triggers a save operation viapostMessage to the builder iframe. This is equivalent to the user clicking the Save button. The save is asynchronous — listen for the template-saved event to confirm completion.
var builder = document.getElementById('template-builder');
// Trigger a save
builder.save();
// Listen for confirmation
builder.addEventListener('template-saved', function (event) {
console.log('Template saved at:', event.detail.updated_at);
});
Fields configuration
Thedata-fields attribute accepts a JSON array of field definitions. Each field object supports the following properties:
| Property | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Display name of the field |
type | string | Yes | Field type (see table below) |
role | string | No | Submitter role this field is assigned to |
required | boolean | No | Whether the field must be filled before submission |
options | string[] | No | Available options for select and radio field types |
| Type | Description |
|---|---|
text | Single-line text input |
signature | Signature pad (draw, type, or upload) |
initials | Initials pad |
date | Date picker |
checkbox | Checkbox toggle |
select | Dropdown select |
radio | Radio button group |
number | Numeric input |
file | File upload |
image | Image upload |
stamp | Stamp/seal placement |
cells | Character-per-cell input (for codes, SSNs, etc.) |
phone | Phone number input |
payment | Payment collection field |
phone_verification | Phone + SMS OTP verification |
data-only-defined-fields:
When you set data-only-defined-fields="true", only the field types specified in data-fields appear in the field palette. This is useful when you want to restrict what fields a user can place on the document.
<docuseal-builder
data-token="YOUR_JWT_TOKEN"
data-only-defined-fields="true"
data-fields='[
{"name": "Full Name", "type": "text", "role": "Recipient", "required": true},
{"name": "Signature", "type": "signature", "role": "Recipient", "required": true},
{"name": "Date Signed", "type": "date", "role": "Recipient", "required": true},
{"name": "Agreed", "type": "checkbox", "role": "Recipient", "required": true}
]'
></docuseal-builder>
React integration
import { useEffect, useRef, useCallback } from 'react';
function TemplateBuilder({ token, onSaved, onSend, onChange }) {
const builderRef = useRef(null);
const handleSaved = useCallback((event) => {
const data = event.detail;
console.log('Template saved:', data.template_id, data.template_name);
console.log('Fields:', data.fields.length);
if (onSaved) onSaved(data);
}, [onSaved]);
const handleSend = useCallback((event) => {
const data = event.detail;
console.log('Send clicked for template:', data.template_id);
if (onSend) onSend(data);
}, [onSend]);
const handleUpload = useCallback((event) => {
const data = event.detail;
console.log('Document uploaded:', data.filename, `(${data.page_count} pages)`);
}, []);
const handleChange = useCallback((event) => {
const data = event.detail;
console.log('Builder changed:', data.change_type, 'Fields:', data.fields.length);
if (onChange) onChange(data);
}, [onChange]);
useEffect(() => {
const el = builderRef.current;
if (!el) return;
el.addEventListener('template-saved', handleSaved);
el.addEventListener('send', handleSend);
el.addEventListener('upload', handleUpload);
el.addEventListener('change', handleChange);
return () => {
el.removeEventListener('template-saved', handleSaved);
el.removeEventListener('send', handleSend);
el.removeEventListener('upload', handleUpload);
el.removeEventListener('change', handleChange);
};
}, [handleSaved, handleSend, handleUpload, handleChange]);
async function handleGetTemplate() {
const el = builderRef.current;
if (!el) return;
try {
const template = await el.getTemplate();
console.log('Current template state:', template);
} catch (err) {
console.error('Timeout getting template:', err.message);
}
}
function handleManualSave() {
const el = builderRef.current;
if (el) el.save();
}
return (
<div>
<div style={{ marginBottom: '12px' }}>
<button onClick={handleManualSave}>Save Template</button>
<button onClick={handleGetTemplate}>Get Template State</button>
</div>
<docuseal-builder
ref={builderRef}
data-token={token}
data-language="en"
data-autosave="true"
data-preview="true"
data-with-title="true"
data-with-title-input="true"
data-with-send-button="true"
data-with-recipients="true"
data-with-upload-button="true"
data-with-add-page-button="false"
data-with-sign-yourself-button="true"
data-with-documents-list="true"
data-with-fields-list="true"
data-with-fields-detection="true"
data-with-send-myself-email="false"
data-draw-field-type="text"
data-field-types="text,signature,date,checkbox"
data-custom-button-title="Publish Template"
data-custom-button-url="https://your-app.com/templates/published"
data-email-subject="Please sign: {{template.name}}"
data-email-body="Hi, please review and sign: {{submitter.link}}"
data-background-color="#f9fafb"
data-custom-css="body { font-family: 'Inter', sans-serif; }"
data-input-mode="false"
data-with-signature-id="false"
data-extract-fields="true"
data-roles='["Sender", "Recipient"]'
data-submitters={JSON.stringify([
{ name: 'Recipient', email: 'jane@example.com' }
])}
data-required-fields={JSON.stringify(['Signature', 'Date'])}
data-fields={JSON.stringify([
{ name: 'Full Name', type: 'text', role: 'Recipient', required: true },
{ name: 'Signature', type: 'signature', role: 'Recipient', required: true },
{ name: 'Date', type: 'date', role: 'Recipient', required: true },
{ name: 'Sender Signature', type: 'signature', role: 'Sender', required: true }
])}
/>
</div>
);
}
export default TemplateBuilder;