├── .gitignore ├── LICENSE ├── README.md ├── esbuild.config.js ├── package.json ├── src ├── builder.tsx ├── form.tsx └── index.ts ├── tsconfig.json └── yarn.lock /.gitignore: -------------------------------------------------------------------------------- 1 | /dist 2 | /node_modules 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2025 DocuSeal LLC 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DocuSeal React Components 2 | 3 | [📙 Documentation](https://www.docuseal.com/docs/embedded/form#react) | [💻 Examples](https://github.com/docusealco/docuseal-react-examples) | [🚀 Demo App](https://embed.docuseal.tech/) 4 | 5 | This package provides a convenient way to embed [DocuSeal](https://www.docuseal.com) into React apps. Sign documents and create document forms directly in your apps. 6 | 7 | ![Docuseal Form](https://github.com/docusealco/docuseal-vue/assets/1176367/828f9f53-3131-494c-8e37-5c74fa94cfa8) 8 | 9 | ## Installation 10 | 11 | ```bash 12 | npm install @docuseal/react 13 | ``` 14 | 15 | ## Documentation 16 | 17 | For detailed documentation, please click [here](https://www.docuseal.com/docs/embedded). 18 | 19 | ## Usage 20 | 21 | ### Signing Form 22 | 23 | Copy public DocuSeal form URL from [https://docuseal.com](https://docuseal.com) and use it in the `src` component prop: 24 | 25 | ```jsx 26 | import React from "react" 27 | import { DocusealForm } from '@docuseal/react' 28 | 29 | export function App() { 30 | return ( 31 |
32 | 36 |
37 | ); 38 | } 39 | ``` 40 | 41 | ### Form Builder 42 | #### React Client Render 43 | ```jsx 44 | import React, { useEffect, useState } from 'react' 45 | import { DocusealBuilder } from '@docuseal/react' 46 | 47 | export function App() { 48 | const [token, setToken] = useState() 49 | 50 | useEffect(() => { 51 | fetch('/api/docuseal/builder_token', { 52 | method: 'POST' 53 | }).then(async (resp) => { 54 | const data = await resp.json() 55 | 56 | setToken(data.token) 57 | }) 58 | }, []); 59 | 60 | return ( 61 |
62 | {token && } 63 |
64 | ); 65 | } 66 | ``` 67 | 68 | To protect the template builder from unathorized access a secure token (JWT) should be generated on the back-end: 69 | 70 | ```js 71 | const express = require('express'); 72 | const jwt = require('jsonwebtoken'); 73 | 74 | const app = express(); 75 | 76 | app.post('/api/docuseal/builder_token', (req, res) => { 77 | const token = jwt.sign({ 78 | user_email: 'your-docuseal-user-email@company.com', 79 | integration_email: 'customer@example.com', // replace with current user email 80 | name: 'Integration W-9 Test Form', 81 | document_urls: ['https://www.irs.gov/pub/irs-pdf/fw9.pdf'], 82 | }, process.env.DOCUSEAL_TOKEN); 83 | 84 | res.json({ token }); 85 | }); 86 | 87 | app.listen(8080, () => { 88 | console.log(`Server is running`); 89 | }); 90 | ``` 91 | 92 | Obtain secret API token (`DOCUSEAL_TOKEN` env variable) to sign JWT from [https://console.docuseal.com/api](https://console.docuseal.com/api). 93 | 94 | Find Express.js example project [here](https://github.com/docusealco/docuseal-react-examples/tree/master/expess-app). 95 | 96 | #### Next.js SSR 97 | ```js 98 | import jwt from 'jsonwebtoken'; 99 | import { DocusealBuilder } from '@docuseal/react' 100 | 101 | export default function Home() { 102 | const token = jwt.sign( { 103 | user_email: process.env.DOCUSEAL_USER_EMAIL, 104 | integration_email: 'test@example.com', 105 | name: 'Integration W-9 Test Form', 106 | document_urls: ['https://www.irs.gov/pub/irs-pdf/fw9.pdf'], 107 | }, process.env.DOCUSEAL_TOKEN); 108 | 109 | return ( 110 |
111 |

Docuseal Builder

112 | 113 |
114 | ); 115 | } 116 | ``` 117 | Find Next.js example project [here](https://github.com/docusealco/docuseal-react-examples/tree/master/next-app). 118 | 119 | # License 120 | 121 | MIT 122 | -------------------------------------------------------------------------------- /esbuild.config.js: -------------------------------------------------------------------------------- 1 | const { build } = require("esbuild"); 2 | 3 | const shared = { 4 | bundle: true, 5 | entryPoints: ["./src/index.ts"], 6 | external: ["react"], 7 | logLevel: "info", 8 | minify: true, 9 | sourcemap: false, 10 | }; 11 | 12 | build({ 13 | ...shared, 14 | format: "esm", 15 | outfile: "./dist/index.esm.js", 16 | target: ["esnext", "node12.22.0"], 17 | }); 18 | 19 | build({ 20 | ...shared, 21 | format: "cjs", 22 | outfile: "./dist/index.cjs.js", 23 | target: ["esnext", "node12.22.0"], 24 | }); 25 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@docuseal/react", 3 | "version": "1.0.66", 4 | "description": "DocuSeal React components to integrate documents signing process into apps. ✍️", 5 | "main": "dist/index.cjs.js", 6 | "module": "dist/index.esm.js", 7 | "types": "dist/index.d.ts", 8 | "author": "DocuSeal ", 9 | "license": "MIT", 10 | "homepage": "https://www.docuseal.com/docs/embedded", 11 | "bugs": { 12 | "url": "https://github.com/docusealco/docuseal-react/issues" 13 | }, 14 | "repository": "https://github.com/docusealco/docuseal-react", 15 | "devDependencies": { 16 | "@typescript-eslint/eslint-plugin": "^6.5.0", 17 | "@typescript-eslint/parser": "^6.5.0", 18 | "esbuild": "^0.19.2", 19 | "eslint": "^8.48.0", 20 | "prettier": "^3.0.2", 21 | "typescript": "^5.2.2", 22 | "react": "^18.2.0", 23 | "@types/react": "^18.2.21" 24 | }, 25 | "keywords": [ 26 | "react", 27 | "react-component", 28 | "esignature", 29 | "documents" 30 | ], 31 | "files": [ 32 | "dist", 33 | "!dist/*.tsbuildinfo" 34 | ], 35 | "scripts": { 36 | "build": "node esbuild.config.js && tsc --emitDeclarationOnly --outDir dist", 37 | "lint": "eslint src/**/*.ts --fix", 38 | "format": "prettier --ignore-path .gitignore --write \"**/*.+(js|ts|json)\"" 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/builder.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | export type DocusealBuilderField = { 4 | name: string, 5 | type?: string, 6 | role?: string, 7 | title?: string, 8 | description?: string, 9 | required?: boolean, 10 | readonly?: boolean, 11 | default_value?: string, 12 | width?: number, 13 | height?: number, 14 | options?: string[], 15 | preferences?: { 16 | font_size?: number, 17 | font_type?: "bold" | "italic" | "bold_italic", 18 | mask?: boolean | number, 19 | font?: "Times" | "Helvetica" | "Courier", 20 | color?: "black" | "white" | "blue", 21 | align?: "left" | "center" | "right", 22 | valign?: "top" | "center" | "bottom", 23 | format?: string, 24 | price?: number, 25 | currency?: "USD" | "EUR" | "GBP" | "CAD" | "AUD", 26 | }, 27 | validation?: { pattern?: string, message?: string } 28 | } 29 | 30 | export type DocusealBuilderSubmitter = { 31 | email?: string, 32 | role?: string, 33 | name?: string, 34 | phone?: string, 35 | } 36 | 37 | export type DocusealBuilderProps = { 38 | token: string, 39 | host?: string, 40 | withRecipientsButton?: boolean, 41 | withSendButton?: boolean, 42 | withTitle?: boolean, 43 | withDocumentsList?: boolean, 44 | withFieldsList?: boolean, 45 | withFieldPlaceholder?: boolean, 46 | onlyDefinedFields?: boolean, 47 | preview?: boolean, 48 | previewMode?: boolean, 49 | inputMode?: boolean, 50 | language?: string, 51 | autosave?: boolean, 52 | roles?: string[], 53 | fieldTypes?: string[], 54 | drawFieldType?: string, 55 | fields?: DocusealBuilderField[], 56 | submitters?: DocusealBuilderSubmitter[], 57 | requiredFields?: DocusealBuilderField[], 58 | i18n?: object, 59 | withSignYourselfButton?: boolean, 60 | withUploadButton?: boolean, 61 | withAddPageButton?: boolean, 62 | onLoad?: (detail: any) => void, 63 | onUpload?: (detail: any) => void, 64 | onSend?: (detail: any) => void, 65 | onSave?: (detail: any) => void, 66 | onChange?: (detail: any) => void, 67 | customButton?: { 68 | title: string, 69 | url: string, 70 | }, 71 | emailMessage?: { 72 | subject: string, 73 | body: string 74 | }, 75 | backgroundColor?: string, 76 | saveButtonText?: string, 77 | sendButtonText?: string, 78 | className?: string, 79 | customCss?: string, 80 | style?: React.CSSProperties 81 | } 82 | 83 | const DocusealBuilder = ({ 84 | token, 85 | host = 'cdn.docuseal.com', 86 | language = 'en', 87 | preview = false, 88 | previewMode = false, 89 | inputMode = false, 90 | autosave = true, 91 | withRecipientsButton = true, 92 | withDocumentsList = true, 93 | withFieldsList = true, 94 | withFieldPlaceholder = false, 95 | withSendButton = true, 96 | withTitle = true, 97 | onlyDefinedFields = false, 98 | withSignYourselfButton = true, 99 | withUploadButton = true, 100 | withAddPageButton = false, 101 | roles = [], 102 | fields = [], 103 | submitters = [], 104 | requiredFields = [], 105 | i18n = {}, 106 | fieldTypes = [], 107 | drawFieldType = 'text', 108 | customButton = { title: '', url: '' }, 109 | emailMessage = { subject: '', body: '' }, 110 | backgroundColor = '', 111 | onLoad = () => {}, 112 | onUpload = () => {}, 113 | onSend = () => {}, 114 | onSave = () => {}, 115 | onChange = () => {}, 116 | className = '', 117 | sendButtonText = '', 118 | saveButtonText = '', 119 | customCss = '', 120 | style = {} 121 | }: DocusealBuilderProps): JSX.Element => { 122 | const scriptId = 'docuseal-builder-script' 123 | const scriptSrc = `https://${host}/js/builder.js` 124 | const isServer = typeof window === 'undefined' 125 | const builderRef = isServer ? null : React.useRef(null) 126 | 127 | if (!isServer) { 128 | React.useEffect(() => { 129 | if (!document.getElementById(scriptId)) { 130 | const script = document.createElement('script') 131 | 132 | script.id = scriptId 133 | script.async = true 134 | script.src = scriptSrc 135 | 136 | document.head.appendChild(script) 137 | } 138 | }, []) 139 | 140 | React.useEffect(() => { 141 | const el = builderRef?.current 142 | 143 | const handleSend = (e: Event) => onSend && onSend((e as CustomEvent).detail) 144 | 145 | if (el) { 146 | el.addEventListener('send', handleSend) 147 | } 148 | 149 | return () => { 150 | if (el) { 151 | el.removeEventListener('send', handleSend) 152 | } 153 | } 154 | }, [onSend]) 155 | 156 | React.useEffect(() => { 157 | const el = builderRef?.current 158 | 159 | const handleLoad = (e: Event) => onLoad && onLoad((e as CustomEvent).detail) 160 | 161 | if (el) { 162 | el.addEventListener('load', handleLoad) 163 | } 164 | 165 | return () => { 166 | if (el) { 167 | el.removeEventListener('load', handleLoad) 168 | } 169 | } 170 | }, [onLoad]) 171 | 172 | React.useEffect(() => { 173 | const el = builderRef?.current 174 | 175 | const handleUpload = (e: Event) => onUpload && onUpload((e as CustomEvent).detail) 176 | 177 | if (el) { 178 | el.addEventListener('upload', handleUpload) 179 | } 180 | 181 | return () => { 182 | if (el) { 183 | el.removeEventListener('upload', handleUpload) 184 | } 185 | } 186 | }, [onUpload]) 187 | 188 | React.useEffect(() => { 189 | const el = builderRef?.current 190 | 191 | const handleSave = (e: Event) => onSave && onSave((e as CustomEvent).detail) 192 | 193 | if (el) { 194 | el.addEventListener('save', handleSave) 195 | } 196 | 197 | return () => { 198 | if (el) { 199 | el.removeEventListener('save', handleSave) 200 | } 201 | } 202 | }, [onSave]) 203 | 204 | React.useEffect(() => { 205 | const el = builderRef?.current 206 | 207 | const handleChange = (e: Event) => onChange && onChange((e as CustomEvent).detail) 208 | 209 | if (el) { 210 | el.addEventListener('change', handleChange) 211 | } 212 | 213 | return () => { 214 | if (el) { 215 | el.removeEventListener('change', handleChange) 216 | } 217 | } 218 | }, [onChange]) 219 | } 220 | 221 | const booleanToAttr = (value: any) => value === true ? 'true' : (value === false ? 'false' : value) 222 | 223 | return ( 224 | <> 225 | {React.createElement('docuseal-builder', { 226 | 'data-token': token, 227 | 'data-preview': booleanToAttr(preview || previewMode), 228 | 'data-input-mode': inputMode, 229 | 'data-language': language, 230 | 'data-autosave': booleanToAttr(autosave), 231 | 'data-send-button-text': sendButtonText, 232 | 'data-save-button-text': saveButtonText, 233 | 'data-roles': roles.join(','), 234 | 'data-field-types': fieldTypes.join(','), 235 | 'data-draw-field-type': drawFieldType, 236 | 'data-fields': JSON.stringify(fields), 237 | 'data-submitters': JSON.stringify(submitters), 238 | 'data-required-fields': JSON.stringify(requiredFields), 239 | 'data-i18n': JSON.stringify(i18n), 240 | 'data-custom-button-title': customButton.title, 241 | 'data-custom-button-url': customButton.url, 242 | 'data-email-subject': emailMessage.subject, 243 | 'data-email-body': emailMessage.body, 244 | 'data-with-recipients-button': booleanToAttr(withRecipientsButton), 245 | 'data-with-send-button': booleanToAttr(withSendButton), 246 | 'data-with-documents-list': booleanToAttr(withDocumentsList), 247 | 'data-with-fields-list': booleanToAttr(withFieldsList), 248 | 'data-with-field-placeholder': booleanToAttr(withFieldPlaceholder), 249 | 'data-with-title': booleanToAttr(withTitle), 250 | 'data-only-defined-fields': booleanToAttr(onlyDefinedFields), 251 | 'data-with-upload-button': booleanToAttr(withUploadButton), 252 | 'data-with-add-page-button': booleanToAttr(withAddPageButton), 253 | 'data-with-sign-yourself-button': booleanToAttr(withSignYourselfButton), 254 | 'data-background-color': backgroundColor, 255 | 'data-custom-css': customCss, 256 | ref: builderRef, 257 | className, 258 | style, 259 | })} 260 | {isServer &&