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

Requirements

  • Vue 3.3.0+

Installation

npm install @sajn/embed-vue

Component

Import

<script setup>
import { EmbedSignDocument } from '@sajn/embed-vue';
</script>

Props

PropTypeRequiredDefaultDescription
document-idstringYes-Document ID
tokenstringYes-Signer token
hoststringNohttps://app.sajn.seCustom host URL
languagestringNo-UI language (sv, en, no, da, fi, de, is, es, fr, it)
classstringNo-CSS class for iframe
css-varsCssVarsNo-Theme customization
allow-document-rejectionbooleanNofalseEnable rejection
show-scroll-indicatorbooleanNotrueShow scroll indicator
additional-propsobjectNo-Additional options

Events

EventPayloadDescription
document-ready-Iframe loaded and ready
signer-completedSignerCompletedDataSigning completed
signer-rejectedSignerRejectedDataDocument rejected
document-errorDocumentErrorDataError occurred

Basic Example

<script setup>
import { EmbedSignDocument } from '@sajn/embed-vue';

const props = defineProps<{
  documentId: string;
  token: string;
}>();

function handleComplete(data) {
  console.log('Document signed!', data);
}
</script>

<template>
  <div class="signing-container">
    <EmbedSignDocument
      :document-id="props.documentId"
      :token="props.token"
      @signer-completed="handleComplete"
    />
  </div>
</template>

<style scoped>
.signing-container {
  width: 100%;
  height: 100vh;
}
</style>

Full Example with All Props

<script setup lang="ts">
import { EmbedSignDocument } from '@sajn/embed-vue';
import type { SignerCompletedData, SignerRejectedData } from '@sajn/embed-vue';

const props = defineProps<{
  documentId: string;
  token: string;
}>();

const cssVars = {
  primary: '#2563eb',
  background: '#ffffff',
  foreground: '#1f2937',
  mutedForeground: '#6b7280',
};

function handleReady() {
  console.log('Signing interface loaded');
}

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

function handleRejected(data: SignerRejectedData) {
  console.log('Document rejected:', data.reason);
}

function handleError(error: { code: string; message: string }) {
  console.error('Error:', error.code, error.message);
}
</script>

<template>
  <div class="signing-container">
    <EmbedSignDocument
      :document-id="props.documentId"
      :token="props.token"
      :css-vars="cssVars"
      :allow-document-rejection="true"
      class="signing-iframe"
      @document-ready="handleReady"
      @signer-completed="handleComplete"
      @signer-rejected="handleRejected"
      @document-error="handleError"
    />
  </div>
</template>

<style scoped>
.signing-container {
  width: 100%;
  height: 100vh;
}

.signing-iframe {
  border-radius: 8px;
  overflow: hidden;
}
</style>

TypeScript Types

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

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;
}

Nuxt

The component works with Nuxt 3 projects:
<!-- pages/sign/[id].vue -->
<script setup lang="ts">
import { EmbedSignDocument } from '@sajn/embed-vue';

const route = useRoute();
const documentId = route.params.id as string;

// Fetch token from your API
const { data: token } = await useFetch(`/api/sign/${documentId}/token`);

function handleComplete() {
  navigateTo('/thank-you');
}
</script>

<template>
  <div class="signing-page">
    <EmbedSignDocument
      v-if="token"
      :document-id="documentId"
      :token="token"
      @signer-completed="handleComplete"
    />
  </div>
</template>

<style scoped>
.signing-page {
  height: 100vh;
}
</style>

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

<script setup>
import { EmbedViewDocument } from '@sajn/embed-vue';
</script>

Props

PropTypeRequiredDefaultDescription
document-idstringYes-Document ID
tokenstringYes-Viewer token
hoststringNohttps://app.sajn.seCustom host URL
languagestringNo-UI language (sv, en, no, da, fi, de, is, es, fr, it)
classstringNo-CSS class for iframe
css-varsCssVarsNo-Theme customization
show-scroll-indicatorbooleanNotrueShow scroll indicator
additional-propsobjectNo-Additional options

Events

EventPayloadDescription
document-ready-Iframe loaded and ready
document-errorDocumentErrorDataError occurred

Example

<script setup lang="ts">
import { EmbedViewDocument } from '@sajn/embed-vue';

const props = defineProps<{
  documentId: string;
  token: string;
}>();

function handleReady() {
  console.log('Document loaded');
}

function handleError(error: { code: string; message: string }) {
  console.error('Error:', error.code, error.message);
}
</script>

<template>
  <div class="viewer-container">
    <EmbedViewDocument
      :document-id="props.documentId"
      :token="props.token"
      :css-vars="{
        primary: '#2563eb',
        background: '#ffffff',
      }"
      @document-ready="handleReady"
      @document-error="handleError"
    />
  </div>
</template>

<style scoped>
.viewer-container {
  width: 100%;
  height: 100vh;
}
</style>