Install
$ agentstack add skill-lubusin-frappe-skills-frappe-web-forms ✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Verified badge
Passed review? Show it. Paste this badge into your README, it links to the public security report.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →About
Frappe Web Forms
Build public-facing web forms for data collection, submissions, and customer self-service.
When to use
- Creating forms for external users (no Desk access)
- Building support/ticket submission forms
- Collecting customer feedback or registrations
- Enabling self-service data entry portals
- Replacing simple portal pages with form-based workflows
Inputs required
- Target DocType for form submissions
- Which fields to expose on the web form
- Authentication requirements (login required vs guest)
- Whether users can edit/resubmit entries
- File upload requirements
Procedure
0) Prerequisites
Ensure the target DocType exists and has the fields you want to expose.
1) Create the Web Form
- Type "new web form" in the awesomebar
- Enter a Title (becomes the URL slug)
- Select the DocType for record creation
- Add introduction text (optional, shown above the form)
- Click "Get Fields" to import all fields, or add fields manually
- Set field order and which are required
- Publish the form
2) Configure settings
| Setting | Purpose | |---------|---------| | Login Required | Require authentication before form access | | Allow Edit | Let users edit their submitted entries | | Allow Multiple | Let users submit more than one entry | | Show as Card | Display in card layout style | | Max Attachment Size | Limit file upload sizes | | Success URL | Redirect after successful submission | | Success Message | Custom message after submission |
3) Make it a Standard Web Form (app-bundled)
Check "Is Standard" (visible in Developer Mode) to export the form as files:
my_app/
└── my_module/
└── web_form/
└── contact_us/
├── contact_us.json # Web form metadata
├── contact_us.py # Server-side customization
└── contact_us.js # Client-side customization
4) Add server-side customization
# contact_us.py
import frappe
def get_context(context):
"""Add custom context variables to the web form."""
context.categories = frappe.get_all("Support Category",
filters={"enabled": 1},
fields=["name", "label"],
order_by="label asc"
)
def validate(doc):
"""Custom validation before the document is saved."""
if not doc.email:
frappe.throw("Email address is required")
# Prevent duplicate submissions
existing = frappe.db.exists("Support Ticket", {"email": doc.email, "status": "Open"})
if existing:
frappe.throw("You already have an open ticket. Please wait for a response.")
5) Add client-side customization
// contact_us.js
frappe.ready(function() {
// Handle field changes
frappe.web_form.on("field_change", function(field, value) {
if (field === "category" && value === "Urgent") {
frappe.web_form.set_df_property("description", "reqd", 1);
}
});
// Custom validation
frappe.web_form.validate = function() {
let data = frappe.web_form.get_values();
if (data.phone && !data.phone.match(/^\+?[0-9\-\s]+$/)) {
frappe.msgprint("Please enter a valid phone number");
return false;
}
return true;
};
// Custom after-save behavior
frappe.web_form.after_save = function() {
frappe.msgprint("Thank you for your submission!");
};
});
6) Control permissions
- Guest access: Uncheck "Login Required" for fully public forms
- Portal roles: Assign portal roles to control which logged-in users see the form
- User permissions: Set explicit document-level permissions on the target DocType
- Row-level access: Use User Permission rules to restrict which records users can edit
7) Style the web form
Web forms use the website theme by default. For custom styling:
.web-form-container { max-width: 600px; margin: 0 auto; }
.web-form-container .form-group { margin-bottom: 1.5rem; }
.web-form-container .btn-primary { background-color: #2490EF; }
Verification
- [ ] Web form accessible at the correct URL (
/contact-us) - [ ] All fields render correctly
- [ ] Required field validation works
- [ ] Submission creates the correct DocType record
- [ ] Login requirement enforced (if configured)
- [ ] Edit and resubmit work (if configured)
- [ ] File uploads work within size limits
- [ ] Success message/redirect works after submission
- [ ] Custom Python validation runs on submit
Failure modes / debugging
- Form not accessible: Check if published; verify URL slug
- Permission denied on submit: Check DocType permissions for Website User or Guest
- Fields not showing: Ensure fields are added to the Web Form (not just on the DocType)
- Custom JS not loading: Check browser console; ensure file path is correct
- Validation not firing: Verify
validatefunction in Python file returns/throws correctly - Duplicate entries: Check "Allow Multiple" setting; add custom duplicate detection
Escalation
- For DocType schema →
frappe-doctype-development - For Frappe UI portal apps →
frappe-frontend-development - For API endpoint access →
frappe-api-development
References
- [references/web-forms.md](references/web-forms.md) — Web Form creation and customization
Guardrails
- Validate input server-side: Never trust client validation; check in
validate()Python method - Use captcha for public forms: Enable reCAPTCHA for guest-accessible forms to prevent spam
- Sanitize output: Escape user-submitted data when displaying; use
frappe.utils.escape_html() - Limit file uploads: Set max file size and allowed types for attachment fields
- Check rate limits: Consider throttling form submissions from same IP
Common Mistakes
| Mistake | Why It Fails | Fix | |---------|--------------|-----| | Missing DocType permissions | "Permission denied" on submit | Grant Create permission to Website User or Guest role | | Not handling file uploads | Files don't attach to record | Configure Attach field properly; check upload limits | | XSS vulnerabilities | Security risk | Escape user input in display; use | e filter in templates | | Forgetting to publish form | 404 error | Check "Published" checkbox in Web Form | | Client-only validation | Invalid data in database | Add validate() method in web form Python file | | Not testing as guest user | Works for admin, fails for users | Test in incognito/logged out mode |
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: lubusIN
- Source: lubusIN/frappe-skills
- License: MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet, be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.