Skip to main content
The @sajn/embed-react package provides a React component for embedding the signing experience.

Demo

Requirements

  • React 18.0+ or React 19.0+

Installation

npm install @sajn/embed-react

Component

Import

import { EmbedSignDocument } from '@sajn/embed-react';

Props Interface

type EmbedSignDocumentProps = {
  // Required
  token: string;
  documentId: string;

  // Optional styling
  className?: string;
  cssVars?: CssVars & Record<string, string>;

  // Configuration
  host?: string;  // Default: 'https://app.sajn.se'
  language?: 'sv' | 'en' | 'no' | 'da' | 'fi' | 'de' | 'is' | 'es' | 'fr' | 'it';
  allowDocumentRejection?: boolean;
  showScrollIndicator?: boolean;  // Default: true
  additionalProps?: Record<string, string | number | boolean>;

  // Callbacks
  onDocumentReady?: () => void;
  onSignerCompleted?: (data: SignerCompletedData) => void;
  onSignerRejected?: (data: SignerRejectedData) => void;
  onDocumentError?: (data: { code: string; message: string }) => void;
}

Basic Example

import { EmbedSignDocument } from '@sajn/embed-react';

export function SigningPage({ documentId, token }) {
  return (
    <div style={{ width: '100%', height: '100vh' }}>
      <EmbedSignDocument
        documentId={documentId}
        token={token}
        onSignerCompleted={(data) => {
          console.log('Document signed!', data);
        }}
      />
    </div>
  );
}

Full Example with All Props

import { EmbedSignDocument } from '@sajn/embed-react';
import type { SignerCompletedData, SignerRejectedData } from '@sajn/embed-react';

export function SigningPage({ documentId, token }: { documentId: string; token: string }) {
  const handleReady = () => {
    console.log('Signing interface loaded');
  };

  const handleComplete = (data: SignerCompletedData) => {
    if (data.failed) {
      console.error('Signing failed:', data.failed);
      return;
    }
    console.log('Document signed successfully!');
    // Redirect to success page
  };

  const handleRejected = (data: SignerRejectedData) => {
    console.log('Document rejected:', data.reason);
    // Handle rejection
  };

  const handleError = (error: { code: string; message: string }) => {
    console.error('Error:', error.code, error.message);
    // Show error UI
  };

  return (
    <div className="signing-container">
      <EmbedSignDocument
        documentId={documentId}
        token={token}
        className="signing-iframe"
        cssVars={{
          primary: '#2563eb',
          background: '#ffffff',
          foreground: '#1f2937',
          mutedForeground: '#6b7280',
        }}
        allowDocumentRejection={true}
        onDocumentReady={handleReady}
        onSignerCompleted={handleComplete}
        onSignerRejected={handleRejected}
        onDocumentError={handleError}
      />
    </div>
  );
}

TypeScript Types

The package exports all types:
import type {
  EmbedSignDocumentProps,
  EmbedViewDocumentProps,
  SignerCompletedData,
  SignerRejectedData,
  CssVars,
} from '@sajn/embed-react';

SignerCompletedData

interface SignerCompletedData {
  token: string;
  documentId: string;
  signerId: string;
  failed?: string;  // Present if signing failed
}

SignerRejectedData

interface SignerRejectedData {
  token: string;
  documentId: string;
  signerId: string;
  reason: string;
}

CssVars

type CssVars = {
  background?: string;
  primary?: string;
  foreground?: string;
  mutedForeground?: string;
}

Next.js

The component is marked with "use client" and works in Next.js App Router projects.
// app/sign/[id]/page.tsx
import { EmbedSignDocument } from '@sajn/embed-react';

export default function SignPage({ params }: { params: { id: string } }) {
  // Fetch token from your API
  const token = 'signer_token_from_api';

  return (
    <main style={{ height: '100vh' }}>
      <EmbedSignDocument
        documentId={params.id}
        token={token}
        onSignerCompleted={() => {
          // Use next/navigation to redirect
        }}
      />
    </main>
  );
}
If you’re using the Pages Router, the component works without any additional configuration.

Styling

The component renders an iframe that fills its container. Set dimensions on the parent element:
.signing-container {
  width: 100%;
  height: 600px;
  /* or */
  height: 100vh;
}
The iframe has no border by default and is set to width: 100% and height: 100%.

View Component

The package also includes EmbedViewDocument for displaying signed documents in read-only mode.

Import

import { EmbedViewDocument } from '@sajn/embed-react';

Props Interface

type EmbedViewDocumentProps = {
  // Required
  token: string;
  documentId: string;

  // Optional styling
  className?: string;
  cssVars?: CssVars & Record<string, string>;

  // Configuration
  host?: string;  // Default: 'https://app.sajn.se'
  language?: 'sv' | 'en' | 'no' | 'da' | 'fi' | 'de' | 'is' | 'es' | 'fr' | 'it';
  showScrollIndicator?: boolean;  // Default: true
  additionalProps?: Record<string, string | number | boolean>;

  // Callbacks
  onDocumentReady?: () => void;
  onDocumentError?: (data: { code: string; message: string }) => void;
}

Basic Example

import { EmbedViewDocument } from '@sajn/embed-react';

export function ViewPage({ documentId, token }) {
  return (
    <div style={{ width: '100%', height: '100vh' }}>
      <EmbedViewDocument
        documentId={documentId}
        token={token}
        onDocumentReady={() => {
          console.log('Document loaded');
        }}
      />
    </div>
  );
}

Full Example with All Props

import { EmbedViewDocument } from '@sajn/embed-react';

export function ViewPage({ documentId, token }: { documentId: string; token: string }) {
  const handleReady = () => {
    console.log('Document viewer loaded');
  };

  const handleError = (error: { code: string; message: string }) => {
    console.error('Error:', error.code, error.message);
  };

  return (
    <div className="viewer-container">
      <EmbedViewDocument
        documentId={documentId}
        token={token}
        className="viewer-iframe"
        cssVars={{
          primary: '#2563eb',
          background: '#ffffff',
          foreground: '#1f2937',
          mutedForeground: '#6b7280',
        }}
        onDocumentReady={handleReady}
        onDocumentError={handleError}
      />
    </div>
  );
}