A complete guide to building custom field types from scratch.
Step 1: Register the Type
php
add_filter( 'formforge_field_types', function( $types ) {
$types['signature'] = [
'label' => 'Signature Pad',
'icon' => 'edit',
'category' => 'advanced',
];
return $types;
} );Step 2: Render the Field
php
add_filter( 'formforge_field_render', function( $html, $field ) {
if ( ( $field['type'] ?? '' ) !== 'signature' ) {
return $html;
}
$name = 'formforge_field_' . ( $field['id'] ?? '' );
$label = esc_html( $field['label'] ?? 'Signature' );
$req_mark = ! empty( $field['required'] )
? ' <span class="formforge-required">*</span>' : '';
$required = ! empty( $field['required'] ) ? 'required' : '';
$html = '<div class="formforge-field formforge-field--signature">';
$html .= '<label class="formforge-label">' . $label . $req_mark . '</label>';
$html .= '<canvas id="' . esc_attr( $name ) . '_canvas" width="400" height="150"'
. ' style="border:1.5px solid #e2e8f0;border-radius:8px;cursor:crosshair;'
. 'background:#fafafa;"></canvas>';
$html .= '<input type="hidden" name="' . esc_attr( $name ) . '"'
. ' id="' . esc_attr( $name ) . '" ' . $required . '>';
$html .= '<button type="button" class="ff-clear-signature"'
. ' onclick="clearSignature('' . esc_js( $name ) . '')">'
. 'Clear</button>';
$html .= '</div>';
return $html;
}, 10, 2 );Step 3: Handle Sanitization
php
add_filter( 'formforge_sanitize_field', function( $value, $field ) {
if ( ( $field['type'] ?? '' ) === 'signature' ) {
if ( strpos( $value, 'data:image/png;base64,' ) === 0 ) {
if ( strlen( $value ) > 700000 ) {
return '';
}
return $value;
}
return '';
}
return $value;
}, 10, 2 );Step 4: Enqueue Frontend JavaScript
php
add_action( 'wp_enqueue_scripts', function() {
if ( ! class_exists( 'FORMFORGE_Core' ) ) return;
wp_enqueue_script(
'ff-signature-field',
get_template_directory_uri() . '/js/signature-field.js',
[ 'formforge-frontend' ],
'1.0.0',
true
);
} );javascript
// js/signature-field.js
document.addEventListener( 'DOMContentLoaded', function() {
document.querySelectorAll( '.formforge-field--signature canvas' ).forEach( function( canvas ) {
var ctx = canvas.getContext( '2d' );
var drawing = false;
var hiddenInput = canvas.parentElement.querySelector( 'input[type="hidden"]' );
canvas.addEventListener( 'mousedown', function() { drawing = true; ctx.beginPath(); } );
canvas.addEventListener( 'mousemove', function( e ) {
if ( ! drawing ) return;
var rect = canvas.getBoundingClientRect();
ctx.lineTo( e.clientX - rect.left, e.clientY - rect.top );
ctx.stroke();
} );
canvas.addEventListener( 'mouseup', function() {
drawing = false;
hiddenInput.value = canvas.toDataURL( 'image/png' );
} );
} );
} );
function clearSignature( name ) {
var canvas = document.getElementById( name + '_canvas' );
var ctx = canvas.getContext( '2d' );
ctx.clearRect( 0, 0, canvas.width, canvas.height );
document.getElementById( name ).value = '';
}—