smithery.ai

frappe-web-form-builder

Generate Frappe Web Forms for public-facing forms. Use when creating customer portals, registration forms, surveys, or public data collection forms.

First seen Mar 25, 2026

Installation

$ npx skills add https://smithery.ai

Similar popular skills

Related neighbors and high-traction skills in the same topics — useful to compare before installing.

Also in this package

Other skills from smithery.ai · top by installs.

npx skills add https://smithery.ai

Browse all from smithery.ai

More details

Agent compatibility

Declared targets from SKILL.md / docs. Unmarked agents are not listed — the skill may still install via the CLI.

Claude Code Not declared
Cursor Not declared
Codex Not declared
GitHub Copilot Not declared
Windsurf Not declared
Gemini CLI Not declared
Cline Not declared
OpenCode Not declared

Package contents

Files included with this skill beyond the listing page.

  • skill md SKILL.md 6,453 B
  • docs SUMMARY.md 179 B

History

  1. First seen on skills.sh
  2. First recorded snapshot · 1 installs

SKILL.md

Frappe Web Form Builder

Generate public-facing web forms with validation, file uploads, and integration with Frappe DocTypes.

Global Rules

These Frappe conventions apply to everything this skill generates, and override any conflicting example below.

  • Bench commands: use bare bench (never ./env/bin/bench or a full path). Always pass --site <site> explicitly — never run a bare bench migrate / bench run-tests. Run bench start in the background and only if it isn't already running. Don't run discovery commands (which bench, bench --version).
  • DocType files live at apps/<app>/<app>/<module>/doctype/<name>/<name>.json — the app name appears twice (directory + Python package) — with an empty init.py alongside. Never mkdir the folder; write the JSON and run bench --site <site> migrate to create the structure. Don't add creation, modified, owner, modified_by, or docstatus as fields — Frappe manages them.
  • Database & ORM: prefer frappe.qb.getquery() over raw frappe.db.sql(). Use frappe.db.getall() for server logic (ignores permissions) and frappe.db.getlist() for user-facing APIs (enforces them). Never use frappe.db.setvalue() on a field with validation or lifecycle logic — load the doc and doc.save() so controller hooks run. Batch-fetch related records; never query inside a loop (N+1).
  • Never call frappe.db.commit() in controllers, request handlers, background jobs, or patches — Frappe auto-commits on success and rolls back on uncaught errors. Flush manually only to make a write visible to a subsequent frappe.enqueue() (or pass enqueueaftercommit=True).
  • Permissions & APIs: put permission checks inside controller methods (enforced on every call path), not in API wrappers. Type-hint every @frappe.whitelist() parameter so Frappe validates and casts it, and pass methods=[...] to pin the HTTP verb.

When to Use This Skill

Claude should invoke this skill when:

  • User wants to create public forms
  • User needs customer-facing forms
  • User mentions web forms, surveys, or registration
  • User wants portal functionality
  • User needs forms without login

Access & Security

  • Guest vs authenticated: set "loginrequired": 1 to require a logged-in user (the submission is tied to frappe.session.user); set "loginrequired": 0 to allow anonymous/Guest submissions. Use anonymous access only for genuinely public intake (registration, surveys, contact).
  • DocType permissions still apply. A Web Form is a thin layer over its doctype. The underlying DocType's permission rules govern who can create/read/edit records — exposing a form does not bypass them. For Guest submissions, the Guest role needs create permission on the DocType (commonly via hasweb_view/web permissions), so review those before publishing.
  • Never expose sensitive fields (internal status, approval flags, pricing, owner/role fields) in webformfields on a public form. Only list the fields the submitter should fill.
  • Validate on the server. Web Form client scripts (and reqd/depends_on in the JSON) are UX only and can be bypassed. Authoritative validation must live in the target DocType's controller validate() method.
  • Success handling: successmessage shows inline confirmation text after submit; successurl redirects to another route instead (e.g. /thank-you). Set one or the other to control the post-submit experience.

Capabilities

1. Web Form JSON

Customer Registration Form:

{
  "name": "Customer Registration",
  "route": "customer-registration",
  "title": "Register as Customer",
  "doc_type": "Customer",
  "is_standard": 0,
  "login_required": 0,
  "allow_multiple": 1,
  "show_sidebar": 0,
  "success_url": "/thank-you",
  "success_message": "Registration successful! We'll contact you soon.",
  "introduction_text": "Please fill in your details to register",
  "web_form_fields": [
    {
      "fieldname": "customer_name",
      "label": "Full Name",
      "fieldtype": "Data",
      "reqd": 1
    },
    {
      "fieldname": "email_id",
      "label": "Email",
      "fieldtype": "Data",
      "options": "Email",
      "reqd": 1
    },
    {
      "fieldname": "mobile_no",
      "label": "Phone",
      "fieldtype": "Data",
      "reqd": 1
    },
    {
      "fieldname": "customer_group",
      "label": "Type",
      "fieldtype": "Select",
      "options": "Individual\nCompany",
      "reqd": 1
    },
    {
      "fieldname": "company_name",
      "label": "Company Name",
      "fieldtype": "Data",
      "depends_on": "eval:doc.customer_group=='Company'"
    }
  ]
}

2. Multi-Step Form

Multi-Page Web Form:

{
  "name": "Job Application",
  "is_multi_step": 1,
  "web_form_fields": [
    {
      "fieldname": "step_1",
      "fieldtype": "Section Break",
      "label": "Personal Information"
    },
    {"fieldname": "full_name", "fieldtype": "Data", "reqd": 1},
    {"fieldname": "email", "fieldtype": "Data", "options": "Email", "reqd": 1},

    {
      "fieldname": "step_2",
      "fieldtype": "Section Break",
      "label": "Experience"
    },
    {"fieldname": "years_experience", "fieldtype": "Int"},
    {"fieldname": "current_company", "fieldtype": "Data"},

    {
      "fieldname": "step_3",
      "fieldtype": "Section Break",
      "label": "Documents"
    },
    {"fieldname": "resume", "fieldtype": "Attach", "reqd": 1}
  ]
}

3. Routing

A Web Form is served at its own route (e.g. /customer-registration). If you need a custom public URL that maps to a portal page or DocType instead of the form's default route, add a rule to websiterouterules in the app's hooks.py:

# apps/<app>/<app>/hooks.py
website_route_rules = [
    {"from_route": "/register", "to_route": "customer-registration"},
]

Keep the Web Form route and any websiterouterules entries consistent so a single public path resolves predictably.

References

Web Form Implementation: