Embedded signing in React
Put Verdocs signing inside your own React app: send an envelope from your server, route the signer to your page, render the signer, and know when they are done.
This walks the whole path for a React application: create an envelope from your backend, get the signer to a route you own, render the signing experience inline, and find out when it completes. The signer never leaves your product and never sees a Verdocs URL.
There are two packages involved and they do different jobs. @verdocs/js-sdk talks to the API and belongs on your server. @verdocs/web-sdk-react renders the UI and belongs in your React app.
1. Install
npm install @verdocs/web-sdk-react # your React app
npm install @verdocs/js-sdk # your server2. Create the envelope on your server
Envelope creation needs your API credentials, so it happens server side. Never put a client secret in browser code.
import {VerdocsEndpoint, authenticate, createEnvelope} from '@verdocs/js-sdk';
const endpoint = new VerdocsEndpoint();
// authenticate() returns the tokens but does not attach them, so set it yourself.
const {access_token} = await authenticate(endpoint, {
grant_type: 'client_credentials',
client_id: process.env.VERDOCS_CLIENT_ID!,
client_secret: process.env.VERDOCS_CLIENT_SECRET!,
});
endpoint.setToken(access_token);
const envelope = await createEnvelope(endpoint, {
template_id: '8e0b...c41a',
name: 'Mutual NDA',
recipients: [{role_name: 'Signer', email: 'ada@acme.com', first_name: 'Ada', last_name: 'Lovelace'}],
});See Server workflow with static templates for the full set of options, and Send an envelope without a template if you are uploading a PDF directly instead.
3. Get the signer to your page
The response carries one recipient entry per signer, each with the three values the signing component needs: the envelope ID, the role, and an invite code. Build your own URL from them:
https://yourapp.com/sign/:envelopeId/:roleName/:inviteCodeBy default Verdocs emails the signer a link to its own hosted signing page. To send them to your route instead, suppress the Verdocs notification and send your own message. Bring-your-own notification covers exactly that, and it is the step most people miss on the first try.
The invite code is a bearer credential for that one envelope and that one role. Treat it like a password: it belongs in the link you send the signer and nowhere else.
4. Render the signer
import {useRef} from 'react';
import {useParams} from 'react-router-dom';
import {VerdocsEndpoint} from '@verdocs/js-sdk';
import {VerdocsSign} from '@verdocs/web-sdk-react';
export const SignPage = () => {
const {envelopeId = '', roleName = '', inviteCode = ''} = useParams();
// sessionType: 'signing' means the signer is never asked to log in or create
// an account. Held in a ref so a re-render does not rebuild the session.
const endpoint = useRef(new VerdocsEndpoint({sessionType: 'signing'}));
return (
<VerdocsSign
endpoint={endpoint.current}
envelopeId={envelopeId}
roleId={roleName}
inviteCode={inviteCode}
onEnvelopeUpdated={(e: any) => {
if (e.detail?.event === 'submitted') {
// The signer has finished. Route them wherever makes sense in your app.
}
}}
onSdkError={(e: any) => console.error('Verdocs signing error', e.detail)}
/>
);
};VerdocsSign fills its container, so give it a parent with a real height. It renders the document, the fields, the signature and initial dialogs, and any authentication the recipient requires, including passcode, SMS, ID scan, and KBA. You do not wire those up separately; see the recipient auth ladder for what each one asks of the signer.
The props
| Prop | What it is |
|---|---|
endpoint | A VerdocsEndpoint with sessionType: 'signing'. Omit it and the component builds one with the same default. |
envelopeId | The envelope being signed. |
roleId | The recipient's role name, for example Signer. |
inviteCode | The invite code for that recipient. |
headerTargetId | The ID of a <div> you own. The component moves its header there on first render, which is how you control placement and scroll behavior. Applied once, so do not render that container conditionally. |
toolbarStyle | controls or menu. |
The events
| Event | When it fires |
|---|---|
onEnvelopeLoaded | The envelope is loaded and about to render. Useful for analytics. |
onEnvelopeUpdated | Something changed. Check e.detail.event for submitted to detect completion. |
onSdkError | Something went wrong. Log it; do not leave the signer staring at an empty box. |
5. Confirm completion on your server
onEnvelopeUpdated fires in the signer's browser, which makes it right for updating your UI and wrong for anything that matters. A browser can close mid-submit and a client event can be forged.
Use webhooks for the authoritative signal, and read Securing your webhooks before you trust the payload. Treat the client event as a hint and the webhook as the fact.
One thing worth knowing: an envelope can report complete before the signed PDF and audit certificate have finished generating. If your flow downloads the document immediately on completion, wait for the event that says the artifacts are ready rather than the one that says signing finished. The FAQ covers the distinction.
Branding
Signing inherits the brand on the envelope's organization, so colors, logo, and disclaimers follow without any work in your app. To match your own styles more closely, override the CSS variables:
:root {
--verdocs-primary-color: #654dcb;
--verdocs-button-radius: 999px;
--verdocs-field-background: #8dc63f1a;
}Styling has the full variable reference.
If you cannot use Web Components
Some environments, older Angular versions and WordPress among them, cannot host custom elements. IFRAME embeds gives you the same flow with less styling control.
Vue, Angular, and vanilla
The same component ships for every framework; only the wrapper import changes. Get started with the Web SDK covers the others, and Components is a live Storybook of everything available.