Vue Signature Pad: Capture Signatures in Vue 3 and Nuxt

    Updated · 8 min read

    Vue apps collect signatures in checkout flows, delivery confirmations, consent forms, and contracts. This guide shows how to add a signature pad to a Vue 3 app with AutographJS: a Composition API component with undo and redo, validation, PNG and SVG export, an upload to your API, and the one setting you need for Nuxt. If you work in React instead, read the React signature pad guide.

    1. Install the package

    bash
    npm install autographjs

    The Vue component is exported from autographjs/vue. Vue 3 is a peer dependency, so the package uses the Vue version already in your project. TypeScript types ship with the package.

    2. Add the signature pad component

    Import SignaturePad and keep a template ref to it. The ref exposes the pad's methods, typed as SignaturePadInstance:

    vue
    <script setup lang="ts">
    import { ref } from "vue";
    import { SignaturePad } from "autographjs/vue";
    import type { SignaturePadInstance } from "autographjs/vue";
    
    const pad = ref<SignaturePadInstance | null>(null);
    </script>
    
    <template>
      <SignaturePad
        ref="pad"
        :width="600"
        :height="200"
        stroke-color="#1a1a2e"
        :stroke-width="2.5"
        background-color="#ffffff"
      />
    </template>

    width and height set the drawing resolution (600 by 200 by default). The canvas is styled to fill its container up to that width with the same aspect ratio, and it sets touch-action: none, so drawing on a phone does not scroll the page. Try the brushes and colors in the online signature pad demo before you pick defaults.

    3. Undo, redo, and clear

    History works per stroke, so undo removes the last line rather than the whole signature:

    vue
    <template>
      <div class="signature">
        <label>Sign here</label>
        <SignaturePad ref="pad" />
        <div class="actions">
          <button type="button" @click="pad?.undo()">Undo</button>
          <button type="button" @click="pad?.redo()">Redo</button>
          <button type="button" @click="pad?.clear()">Clear</button>
        </div>
      </div>
    </template>

    Keep type="button" on these buttons when the pad is inside a form, otherwise each click submits it.

    4. Listen for changes

    The component emits change with a PNG data URL and end when the pointer is released or leaves the canvas. That is handy for a live preview or for saving a draft:

    vue
    <script setup lang="ts">
    import { ref } from "vue";
    import { SignaturePad } from "autographjs/vue";
    
    const preview = ref("");
    
    // "change" receives a PNG data URL when the pointer is released or leaves the canvas
    function onChange(png: string) {
      preview.value = png;
    }
    </script>
    
    <template>
      <SignaturePad @change="onChange" />
      <img v-if="preview" :src="preview" alt="Signature preview" />
    </template>

    Because change also fires when the pointer simply leaves the canvas, do not treat it as proof that the user signed. Check isEmpty() instead, as in the next step. Calling clear(), undo(), or redo() from code does not emit change, so update any preview yourself after those calls.

    5. Validate the signature before submitting

    vue
    <script setup lang="ts">
    import { ref } from "vue";
    import { SignaturePad } from "autographjs/vue";
    import type { SignaturePadInstance } from "autographjs/vue";
    
    const pad = ref<SignaturePadInstance | null>(null);
    const error = ref("");
    
    function submit() {
      if (!pad.value || pad.value.isEmpty()) {
        error.value = "Please sign before submitting.";
        return;
      }
      if (pad.value.getStrokeCount() < 2) {
        error.value = "Your signature looks too short. Please sign again.";
        return;
      }
    
      error.value = "";
      const png = pad.value.toPNG(); // "data:image/png;base64,..."
      // ...send png with the rest of the form
    }
    </script>
    
    <template>
      <form @submit.prevent="submit">
        <SignaturePad ref="pad" />
        <p v-if="error" role="alert">{{ error }}</p>
        <button type="submit">Sign and continue</button>
      </form>
    </template>

    isEmpty() returns true when nothing has been drawn, and getStrokeCount() lets you reject a single accidental tap. Show the error with role="alert" so screen readers announce it.

    6. Brushes, colors, and read-only mode

    Props include brushType ("pen", "marker", "calligraphy", "dotted"), strokeColor, strokeWidth, backgroundColor, pressure, and readOnly:

    vue
    <script setup lang="ts">
    import { ref } from "vue";
    import { SignaturePad } from "autographjs/vue";
    
    const color = ref("#1a1a2e");
    const locked = ref(false);
    </script>
    
    <template>
      <!-- Changing these props re-creates the pad and clears the drawing -->
      <SignaturePad :stroke-color="color" :read-only="locked" brush-type="calligraphy" />
    </template>

    One thing to know: the component watches these props and re-creates the pad when any of them changes, which clears what was drawn. Set the color and brush before the user starts signing, or export the signature first if you let users switch styles midway.

    7. Export to PNG, JPEG, or SVG

    ts
    pad.value?.toPNG();               // PNG data URL
    pad.value?.toJPEG();              // JPEG data URL, smaller, no transparency
    pad.value?.toSVG();               // SVG markup string
    pad.value?.toBase64("image/webp"); // any type the browser's canvas supports

    PNG is the best default for documents. Use JPEG only with a solid backgroundColor, since it has no transparency. SVG keeps the strokes as vectors for printing or scaling.

    8. Send the signature to your API

    The sendToAPI helper from the core package works with the Vue ref. It posts JSON with the signature, its format, a timestamp, and your metadata:

    ts
    import { sendToAPI } from "autographjs";
    
    async function save() {
      if (!pad.value || pad.value.isEmpty()) return;
    
      const response = await sendToAPI(pad.value, {
        url: "/api/signatures",
        format: "png",
        metadata: { documentId: "contract-42" },
      });
    
      if (!response.ok) throw new Error("Could not save the signature");
    }

    Server examples for Node.js / Express and Python / Flask are in the backend integration section of the docs.

    9. Using the signature pad in Nuxt

    The pad draws on a browser canvas, so in Nuxt render it only on the client. Wrap it in <ClientOnly> and give it a fallback with the same height to avoid layout shift:

    vue
    <template>
      <ClientOnly>
        <SignaturePad ref="pad" />
        <template #fallback>
          <div class="signature-placeholder">Loading signature pad…</div>
        </template>
      </ClientOnly>
    </template>
    
    <script setup lang="ts">
    import { ref } from "vue";
    import { SignaturePad } from "autographjs/vue";
    import type { SignaturePadInstance } from "autographjs/vue";
    
    const pad = ref<SignaturePadInstance | null>(null);
    </script>

    Production checklist

    • Check isEmpty() on submit, and reject empty or oversized payloads on the server too.
    • Fix the brush and color before signing starts, because changing them clears the pad.
    • Give the pad a visible label and large tap targets for the controls.
    • Store the signer, timestamp, and document version next to the image.
    • Treat signature images as personal data and serve them only to authorised users.

    FAQ

    Does it work with Vue 2?

    The component is built with Vue 3 APIs (defineComponent, expose). For a Vue 2 project, use the framework-agnostic SignaturePad class from autographjs on a canvas element; the docs show the Vanilla JS setup.

    How do I get the signature as a Base64 string in Vue?

    Call pad.value?.toPNG() or pad.value?.toBase64(). Both return a data URL that starts with data:image/png;base64,.

    Why does my signature disappear when I change the color?

    Changing any pad prop re-creates the pad. Choose the color before the user signs, or export the current signature before switching.

    Next steps

    Read the full API in the documentation, compare with the React version, or get the package on npm and GitHub.