Adding a Custom Button to a Frappe Form (Frappe v15)
Introduction: What Does Adding a Custom Button Mean in Frappe?
Adding a custom button in Frappe means extending a DocType form with a user-triggered action.
In Frappe Framework v15, custom buttons are added using client-side JavaScript and are commonly used to trigger workflows, call server-side methods, or navigate users to related records.
This guide explains how to add a custom button to a form in a technically correct and production-safe way.
When Should You Add a Custom Button?
Custom buttons are useful when you need:
- Manual user actions beyond standard Save / Submit
- Integration triggers (API calls, background jobs)
- Navigation to related DocTypes
- Controlled workflow steps without automation
Common ERPNext use cases
- “Create Delivery Note” from Sales Order
- “Generate Invoice” from custom DocType
- “Sync with External System”
Technical Prerequisites
Before proceeding, ensure you have:
- A running Frappe Framework v15 site
- Access to the app’s public/js directory
- A valid DocType to customize
- Basic knowledge of JavaScript
How to Add a Custom Button to a Form in Frappe v15
Direct Answer (AEO-Optimized)
To add a custom button in Frappe v15, use the frm.add_custom_button() method inside the DocType’s client-side JavaScript file, typically within the refresh event.
Step-by-Step: Adding a Custom Button
Step 1: Locate or Create the Client Script File
For a DocType named Sales Order, the file path is:
your_app/your_app/public/js/sales_order.js
If the file does not exist, create it and ensure it is loaded via hooks or doctype_js.
Step 2: Add the Button Using refresh
frappe.ui.form.on('Sales Order', {
refresh: function(frm) {
frm.add_custom_button(__('My Custom Action'), function() {
frappe.msgprint('Button Clicked');
});
}
});
What this does
- Adds a button labeled My Custom Action
- Displays a message when clicked
- Button appears on every form refresh
Verified for Frappe Framework v15
Adding the Button Only in Specific Conditions
Example: Show Button Only When Document Is Submitted
frappe.ui.form.on('Sales Order', {
refresh: function(frm) {
if (frm.doc.docstatus === 1) {
frm.add_custom_button(__('Generate Report'), function() {
frappe.msgprint('Report Generated');
});
}
}
});
Why this matters
- Prevents invalid user actions
- Maintains workflow discipline
- Improves UX consistency
Grouping Custom Buttons Under a Menu
Why Use Button Groups?
Button groups keep the UI clean when multiple actions exist.
Example: Add Button Under “Actions”
frm.add_custom_button(
__('Export Data'),
function() {
frappe.msgprint('Export Started');
},
__('Actions')
);
This places the button inside a dropdown labeled Actions.
Calling a Server-Side Method from a Custom Button
Use Case
- Trigger backend logic such as:
- Creating linked records
- Running validations
Integrating third-party services
Client Script Example
frm.add_custom_button(__('Process Order'), function() {
frappe.call({
method: 'your_app.api.process_order',
args: {
docname: frm.doc.name
},
callback: function(r) {
if (!r.exc) {
frappe.msgprint('Order Processed Successfully');
}
}
});
});
Server-Side Python Method (v15-Compatible)
import frappe
@frappe.whitelist()
def process_order(docname):
doc = frappe.get_doc('Sales Order', docname)
# business logic here
return True
Uses @frappe.whitelist() as required in v15
No deprecated APIs used
Best Practices for Custom Buttons in Frappe
UI & UX Best Practices
- Avoid too many buttons on refresh
- Use clear, action-oriented labels
- Group related actions logically
Technical Best Practices
- Always wrap logic in conditions
- Avoid heavy logic in client scripts
- Prefer server-side processing for data changes
Common Mistakes to Avoid
| Mistake | Why It’s a Problem |
| Adding buttons outside refresh | Button may not render |
| Heavy logic in JS | Performance issues |
| No permission checks | Security risk |
| Not grouping buttons | Cluttered UI |
Troubleshooting Custom Button Issues
Button Not Showing?
Check:
- Correct DocType name
- JS file loaded properly
- No JavaScript console errors
Button Click Does Nothing?
Verify:
- Function binding
- frappe.call method path
- Server method is whitelisted
Advanced Use Cases
1. Disable Button After Click
frm.add_custom_button(__('Submit Once'), function(btn) {
btn.prop('disabled', true);
});
2. Redirect to Another DocType
frappe.set_route('Form', 'Customer', frm.doc.customer);
Integration Patterns
Custom buttons are commonly used to integrate:
- ERPNext workflows
- External REST APIs
- Background jobs
- Custom reports
They act as controlled manual triggers in enterprise systems.
Target Audience Tags
- ERPNext Developers
- Frappe Framework Developers
- Technical Consultants
- ERP Customization Teams
Industry Relevance
Custom buttons are widely used in:
- Manufacturing ERP workflows
- Finance approval systems
- Sales automation
- Compliance-driven industries
Summary: Why Custom Buttons Matter
Custom buttons in Frappe Framework v15 provide a clean, controlled way to extend form functionality without breaking standard workflows. When implemented correctly, they improve usability, enforce business logic, and support scalable ERP customizations.
This approach is officially supported, upgrade-safe, and production-ready.