Adding Electronic Signatures to a Rails 8 App
Integrating an electronic signature feature can be a great addition when building applications that require document signing. In this tutorial, we will walk through how to add a signature pad to a Rails 8 app using Tailwind CSS, PostgreSQL, and Importmap. We'll leverage the signature_pad JavaScript library to enable users to draw their signatures directly within a form. Step 1: Install Signature Pad Rails 8 with Importmap allows us to pin JavaScript libraries easily. Start by running the following command to install signature_pad: bin/importmap pin signature_pad This command will pin signature_pad and update the importmap.rb file with: pin "signature_pad" # @5.0.4 Step 2: Create a Stimulus Controller Next, we will create a Stimulus controller to handle the signature pad interactions. Run the following command: bin/rails generate stimulus signature_pad Now, open the generated signature_pad_controller.js file and replace its contents with: import { Controller } from "@hotwired/stimulus"; import SignaturePad from "signature_pad"; export default class extends Controller { static targets = ["canvas", "input"]; connect() { this.signaturePad = new SignaturePad( this.canvasTarget, { dotSize: 3, minWidth: 1, maxWidth: 3 } ); console.log("Please add signature before saving."); } disconnect() { this.signaturePad.off(); } clear() { this.signaturePad.clear(); } submit(event) { event.preventDefault(); this.canvasTarget.toBlob((blob) => { const signatureFile = new File([blob], "signature.png", { type: "image/png" }); const dataTransfer = new DataTransfer(); dataTransfer.items.add(signatureFile); this.inputTarget.files = dataTransfer.files; event.target.submit(); }); } } Explanation of the Stimulus Controller static targets = ["canvas", "input"]: Defines the elements our controller interacts with. connect(): Initializes signature_pad on the canvas element. disconnect(): Cleans up the signature pad instance when the controller is removed. clear(): Clears the signature from the canvas when the clear button is clicked. submit(event): Prevents the default form submission. Converts the signature drawn on the canvas to a blob. Creates a File object and adds it to a DataTransfer instance. Assigns the file to the hidden input field so it gets submitted with the form. Step 3: Set Up the Model To start, let’s assume we have a Consent model. We will now add an attached signature: class Consent

Integrating an electronic signature feature can be a great addition when building applications that require document signing. In this tutorial, we will walk through how to add a signature pad to a Rails 8 app using Tailwind CSS, PostgreSQL, and Importmap. We'll leverage the signature_pad JavaScript library to enable users to draw their signatures directly within a form.
Step 1: Install Signature Pad
Rails 8 with Importmap allows us to pin JavaScript libraries easily. Start by running the following command to install signature_pad
:
bin/importmap pin signature_pad
This command will pin signature_pad
and update the importmap.rb
file with:
pin "signature_pad" # @5.0.4
Step 2: Create a Stimulus Controller
Next, we will create a Stimulus controller to handle the signature pad interactions. Run the following command:
bin/rails generate stimulus signature_pad
Now, open the generated signature_pad_controller.js
file and replace its contents with:
import { Controller } from "@hotwired/stimulus";
import SignaturePad from "signature_pad";
export default class extends Controller {
static targets = ["canvas", "input"];
connect() {
this.signaturePad = new SignaturePad(
this.canvasTarget,
{ dotSize: 3, minWidth: 1, maxWidth: 3 }
);
console.log("Please add signature before saving.");
}
disconnect() {
this.signaturePad.off();
}
clear() {
this.signaturePad.clear();
}
submit(event) {
event.preventDefault();
this.canvasTarget.toBlob((blob) => {
const signatureFile = new File([blob], "signature.png", { type: "image/png" });
const dataTransfer = new DataTransfer();
dataTransfer.items.add(signatureFile);
this.inputTarget.files = dataTransfer.files;
event.target.submit();
});
}
}
Explanation of the Stimulus Controller
-
static targets = ["canvas", "input"]
: Defines the elements our controller interacts with. -
connect()
: Initializessignature_pad
on the canvas element. -
disconnect()
: Cleans up the signature pad instance when the controller is removed. -
clear()
: Clears the signature from the canvas when the clear button is clicked. -
submit(event)
:- Prevents the default form submission.
- Converts the signature drawn on the canvas to a
blob
. - Creates a
File
object and adds it to aDataTransfer
instance. - Assigns the file to the hidden input field so it gets submitted with the form.
Step 3: Set Up the Model
To start, let’s assume we have a Consent model. We will now add an attached signature:
class Consent < ApplicationRecord
belongs_to :employee
has_one_attached :signature #Add this line
validates :first_name, :last_name, :phone_number, :email, :date_signed, presence: true
validates :accepted, inclusion: { in: [ true ], message: "must be accepted" }
validates :advisor_authorization, :consent_persistence, :information_confirmation, inclusion: { in: [ true ], message: "must be checked" }
end
Step 4: Update the Form
Now, let's update the consent form to include the signature pad:
<%= form_for @consent, url: wizard_path, method: :put, class: "space-y-6 mb-12", data: { controller: "signature-pad", action: "submit->signature-pad#submit" } do |form| %>
<%= form.label :signature, class: "block text-sm font-normal text-slate-700 mt-8 mb-2" do %>
Client Signature class="text-purple-500">*
<% end %>
<% if consent.persisted? && consent.signature.attached? %>
<%= image_tag consent.signature, class: "max-w-[300px] rounded border border-gray-300 mb-2" %>
<% end %>
<%= form.file_field :signature, data: { signature_pad_target: "input" }, accept: "image/png", hidden: true %>
<%= tag.canvas width: 950, height: 150, data: { signature_pad_target: "canvas" }, class: "rounded-lg border border-gray-300 mb-2 hover:bg-gray-50 cursor-pointer" %>
<%= tag.div class: "flex justify-between" do %>
<%= tag.button "Clear", type: "button", data: { action: "signature-pad#clear" }, class: "bg-gray-200 hover:bg-gray-300 text-gray-500 px-2 py-1 rounded" %>
<% end %>
class="w-1/2 mt-2">
<%= form.label :date_signed, class: "block text-sm font-normal text-slate-700" do %>
Date signed class="text-purple-500">*
<% end %>
class="mt-1">
<%= form.date_field :date_signed, class: "block w-full rounded-lg border-slate-200 shadow-sm focus:border-purple-500 focus:ring-purple-500 sm:text-sm" %>
<% if consent.errors[:date_signed].any? %>
class="mt-2 text-sm text-red-600"><%= consent.errors[:date_signed].join(", ") %>
<% end %>
class="flex justify-between mt-16">
<%= link_to "Go back", previous_wizard_path, class: "px-4 py-2 text-sm font-medium text-gray-700 border border-gray-300 rounded-md hover:bg-gray-100" %>
<%= form.submit "Next step", class: "px-6 py-2 text-sm font-medium text-white bg-purple-500 rounded-md hover:bg-purple-600 focus:outline-none focus:ring focus:ring-purple-300 cursor-pointer" %>
<% end %>
How the Form Uses the Stimulus Controller
-
data: { controller: "signature-pad", action: "submit->signature-pad#submit" }
: Attaches the Stimulus controller to the form and links thesubmit
action to thesubmit
method in the controller. -
Canvas Element (
data: { signature_pad_target: "canvas" }
): This allows the controller to find the canvas for drawing signatures. -
Hidden File Input (
data: { signature_pad_target: "input" }
): Stores the converted signature as an image file for form submission.
Step 5: Testing the Signature Pad
Start your Rails server:
bin/rails server
Navigate to your form page and test drawing a signature. When you submit, the signature should be stored as an attached file in your database.
Conclusion
In this tutorial, we integrated signature_pad
into a Rails 8 application using Stimulus, Tailwind CSS, and PostgreSQL. We built a consent form where users can draw their signatures, store them in Active Storage, and submit them as part of their agreement. This approach can be extended to any document signing feature in a Rails app!
Happy coding!