Integrate the Document Viewer
The Document Viewer is a web component that lets users edit templates and prepare documents for signing — directly inside your application. This guide shows how to connect it to the GraphQL API to build a complete signing workflow.
Overview
A typical integration follows these steps:
- Authenticate — get a token for the viewer
- Upload a template — via the GraphQL API
- Render the viewer — pass the template ID and token
- Capture events — the viewer emits
updateandvalidateevents as the user works - Send the document — call the
sendmutation with the recipient and role data from the viewer
Authentication
The viewer needs a valid token passed to its token attribute. See Authenticate with the API before wiring the viewer into your application.
Compose Mode — Send a Pre-built Document
Compose mode is for workflows where your system already knows the recipients. The user just places signature fields and sends.
1. Render the viewer
<ls-document-viewer
id="viewer"
templateid="dHBsYjQ5YTg5NWQtYWRhMy0xMWYwLWIxZGMtMDY5NzZlZmU0MzIx"
token="YOUR_ACCESS_TOKEN"
mode="compose"
recipients='[
{"email": "jane@example.com", "firstname": "Jane", "lastname": "Smith", "signerIndex": 1},
{"email": "john@example.com", "firstname": "John", "lastname": "Doe", "signerIndex": 2}
]'
filtertoolbox="signature|initials|date|text"
>
<span slot="right-button">
<button id="send-btn" disabled>Send</button>
</span>
</ls-document-viewer>
2. Track validation and role IDs
As the user adds fields, the viewer emits events. You need two things from these events:
validate— tells you whether the template has all required fields (at minimum, one signature per recipient)update— provides the current template state, includingroleIdfor each participant when you need template role mapping
const viewer = document.getElementById('viewer');
const sendBtn = document.getElementById('send-btn');
let isValid = false;
const recipientRoles = {};
viewer.addEventListener('validate', (e) => {
isValid = e.detail.valid;
sendBtn.disabled = !isValid;
});
viewer.addEventListener('update', (e) => {
const roles = e.detail.template.roles;
roles.forEach((role) => {
recipientRoles[role.signerIndex] = role.id;
});
});
The roleId from the update event maps each recipient to their fields on the template. Include it when your send flow needs that mapping.
3. Send the document
When the user clicks send, call the send mutation with the recipient data and role IDs captured from the viewer:
sendBtn.addEventListener('click', async () => {
const token = '<token-from-authentication-guide>';
const recipients = [
{
firstName: 'Jane',
lastName: 'Smith',
email: 'jane@example.com',
role: 'Signer',
roleId: recipientRoles[1],
signerIndex: 1,
order: 1,
},
{
firstName: 'John',
lastName: 'Doe',
email: 'john@example.com',
role: 'Signer',
roleId: recipientRoles[2],
signerIndex: 2,
order: 2,
},
];
const response = await fetch('https://graphql.uk.legalesign.com/graphql', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${token}`,
},
body: JSON.stringify({
query: `mutation SendDocument {
send(input: {
groupId: "YOUR_GROUP_ID"
templateId: "dHBsYjQ5YTg5NWQtYWRhMy0xMWYwLWIxZGMtMDY5NzZlZmU0MzIx"
title: "Contract - Jane Smith & John Doe"
recipients: ${JSON.stringify(recipients).replace(/"(\w+)":/g, '$1:')}
sequentialSigning: true
})
}`,
}),
});
const result = await response.json();
const taskId = result.data.send;
// Poll for completion
console.log('Task started:', taskId);
});
The send mutation returns a task ID. Poll the task query and monitor task.report.status until it reaches COMPLETED or FAILED to confirm document creation has finished.
Editor Mode — Template Creation
Editor mode gives users the full template editing experience — adding roles, placing fields, configuring options. Use this when you want users to build reusable templates.
<ls-document-viewer
id="editor"
templateid="dHBsYjQ5YTg5NWQtYWRhMy0xMWYwLWIxZGMtMDY5NzZlZmU0MzIx"
token="YOUR_ACCESS_TOKEN"
mode="editor"
></ls-document-viewer>
In editor mode, the update event fires whenever the template changes. You can use this to track the template state or show a save indicator:
const editor = document.getElementById('editor');
editor.addEventListener('update', (e) => {
console.log('Template updated:', e.detail);
});
editor.addEventListener('validate', (e) => {
console.log('Template valid:', e.detail.valid);
});
Template changes are saved automatically by the viewer — you don't need to call any GraphQL mutation to persist edits.
React Integration
The React wrapper provides the same functionality with React-style event handling:
import { useState } from 'react';
import { LsDocumentViewer } from 'legalesign-document-viewer-react';
const DocumentCompose = ({ templateId, recipients, token }) => {
const [isValid, setIsValid] = useState(false);
const [roleMap, setRoleMap] = useState({});
const handleValidate = (e) => {
setIsValid(e.detail.valid);
};
const handleUpdate = (e) => {
const roles = e.detail.template.roles;
const map = {};
roles.forEach((role) => {
map[role.signerIndex] = role.id;
});
setRoleMap(map);
};
const handleSend = async () => {
const mappedRecipients = recipients.map((r, i) => ({
...r,
roleId: roleMap[r.signerIndex],
order: i + 1,
}));
const response = await fetch('https://graphql.uk.legalesign.com/graphql', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${token}`,
},
body: JSON.stringify({
query: `mutation SendDocument {
send(input: {
groupId: "YOUR_GROUP_ID"
templateId: "${templateId}"
title: "Document"
recipients: ${JSON.stringify(mappedRecipients).replace(/"(\w+)":/g, '$1:')}
})
}`,
}),
});
const result = await response.json();
console.log('Task ID:', result.data.send);
};
if (!token) return null;
return (
<LsDocumentViewer
templateid={templateId}
token={token}
mode="compose"
recipients={JSON.stringify(recipients)}
onValidate={handleValidate}
onUpdate={handleUpdate}
>
<div slot="right-button">
<button onClick={handleSend} disabled={!isValid}>
Send
</button>
</div>
</LsDocumentViewer>
);
};
Witnesses
To add a witness for a signer, set roleType: "WITNESS" and use a signerIndex of the parent signer's index + 100. For example, a witness for signer 2 has signerIndex: 102:
<ls-document-viewer
mode="compose"
recipients='[
{"email": "signer@example.com", "firstname": "Alice", "lastname": "Jones", "signerIndex": 1},
{"email": "signer2@example.com", "firstname": "Bob", "lastname": "Brown", "signerIndex": 2},
{"email": "witness@example.com", "firstname": "Carol", "lastname": "White", "signerIndex": 102, "roleType": "WITNESS"}
]'
...
></ls-document-viewer>
Key Concepts
| Viewer concept | GraphQL type | Description |
|---|---|---|
templateid attribute | Template | The template being edited or composed |
recipients attribute | RecipientInput | Recipients passed to compose mode |
roleId from update event | Role | Links a recipient to their fields on the template |
validate event | — | Whether the template has all required fields |
send mutation | send | Sends the document after composition |
token attribute | — | Token from authentication |
mode="compose" | — | Streamlined mode for placing fields and sending |
mode="editor" | — | Full template editing mode |
experience in send input | Experience | Controls branding, emails, and signing page — see Experiences explained |
Related
- Document Viewer reference — full attribute and event reference
- send mutation — send a document
- DocumentSendSettingsInput — full send input structure
- Sending methods explained — send vs sendBatch vs sendBulk
- Upload a File as a Template — upload templates via the API
- Authentication — choose an authentication mode