# Embedded signing in React (/docs/guides/tutorials/embedded-signing-in-react)


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`](/docs/reference/SDK/languages/getting-started) talks to the API and belongs on your server. [`@verdocs/web-sdk-react`](/docs/reference/SDK/web/get-started) renders the UI and belongs in your React app.

## 1. Install [#1-install]

```bash
npm install @verdocs/web-sdk-react   # your React app
npm install @verdocs/js-sdk          # your server
```

## 2. Create the envelope on your server [#2-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.

```ts
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](/docs/guides/tutorials/server-workflow-with-static-templates) for the full set of options, and [Send an envelope without a template](/docs/guides/tutorials/send-an-envelope-without-a-template) if you are uploading a PDF directly instead.

## 3. Get the signer to your page [#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/:inviteCode
```

By 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](/docs/guides/tutorials/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 [#4-render-the-signer]

```tsx
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](/docs/guides/tutorials/the-recipient-auth-ladder) for what each one asks of the signer.

### The props [#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 [#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 [#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](/docs/reference/Rest-API/webhooks) for the authoritative signal, and read [Securing your webhooks](/docs/guides/tutorials/securing-your-webhooks-hmac-vs-oauth) 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](/docs/learn/FAQ) covers the distinction.

## Branding [#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:

```css
:root {
  --verdocs-primary-color: #654dcb;
  --verdocs-button-radius: 999px;
  --verdocs-field-background: #8dc63f1a;
}
```

[Styling](/docs/reference/SDK/web/styling) has the full variable reference.

## If you cannot use Web Components [#if-you-cannot-use-web-components]

Some environments, older Angular versions and WordPress among them, cannot host custom elements. [IFRAME embeds](/docs/guides/tutorials/iframe-embeds) gives you the same flow with less styling control.

## Vue, Angular, and vanilla [#vue-angular-and-vanilla]

The same component ships for every framework; only the wrapper import changes. [Get started with the Web SDK](/docs/reference/SDK/web/get-started) covers the others, and [Components](/docs/reference/SDK/web/components) is a live Storybook of everything available.
