Skip to main content

Frappe Page API Guide (v15)

What is a Page in Frappe?

A Page in Frappe is a UI container that renders a custom interface inside the Desk, built without using a DocType. It allows developers to create fully custom layouts, dashboards, and tools using Jinja, JavaScript, and Python backend logic.

A Page is defined in the file system under an app and loads through Desk routing. It has its own controller, templates, and hooks.

Where are Pages used?

Pages are used when UI requirements cannot be solved with standard DocTypes, List Views, or forms. Common use cases include:

  • Analytical dashboards
  • Management consoles
  • Custom workflow screens
  • Reporting pages
  • Data visualization
  • External service integrations

Custom UI tools (e.g., POS, project boards)

How Pages work in Frappe (v15)

The Page architecture is based on:

  • Python controller: Page server logic
  • JS controller: UI logic and events
  • HTML/Jinja template: rendering
  • Route mapping: access from Desk
  • Permissions: enforced through Python

Directory Structure:

your_app/
└─ your_app/your_app/page/
└─ your_page/
├─ __init__.py
├─ your_page.py
├─ your_page.js
└─ your_page.html

How to Create a Page in Frappe?

Step 1: Create Page via Bench

The simplest way to create a Page:

bench new-page your_page

This generates the complete scaffold under your app.

Step 2: Access Your Page

Once installed, open the page through:

https://<site>/app/your-page

OR via Desk Search:

Ctrl + K → Your Page Name

Page Components Breakdown

1. Python Controller (your_page.py)

This is the backend script loaded when the page route is accessed. It receives context from the Desk and injects data into the HTML template.

Example:

import frappe
def get_context(context):
context.data = frappe.get_list("Customer", fields=["name"])

2. Client Script (your_page.js)

This defines the page UI behavior, including events, API calls, and DOM changes.

Example:

frappe.pages['your-page'].on_page_load = function(wrapper) {
let page = frappe.ui.make_app_page({
parent: wrapper,
title: 'Your Custom Page',
single_column: true
});
frappe.call({
method: "your_app.your_app.page.your_page.your_page.get_data",
callback: function(r) {
console.log(r.message);
}
});
}

3. HTML Template (your_page.html)

Defines layout content:

<div class="your-page-content">
<h2>Customer List</h2>
<ul>
{% for row in data %}
<li>{{ row.name }}</li>
{% endfor %}
</ul>
</div>

Routing and Access

Page Route

By default, the page route uses the folder name:

/app/your-page

You can register custom routes in hooks.py:

website_route_rules = [
{"from_route": "/dashboard", "to_route": "your-page"}
]

Adding Actions and UI Elements

Frappe provides multiple UI builders to compose interface components:

Add Button

page.add_primary_action("Refresh", () => location.reload());

Add Menu Item

page.add_menu_item("Help", () => window.open("https://docs.frappe.io"));

Add Field

let field = page.add_field({
fieldtype: "Data",
label: "Search",
fieldname: "search"
});

Fetching Data from Python in Page Script

Direct call from JS

frappe.call({
method: "your_app.your_app.page.your_page.your_page.get_context",
args: {},
callback(r) {
console.log(r.message);
}
});

Python Side

@frappe.whitelist()
def get_context():
return frappe.get_all("Customer", fields=["name", "customer_name"])

Page Permission Handling

Permission rules depend on:

  • User role assignment
  • Page-level permission defined in metadata
  • Custom server-side validation

Recommended security flow:

  1. validate roles in Python
  2. do not expose sensitive data via client code
  3. use frappe.whitelist() only when needed

Example:

if "System Manager" not in frappe.get_roles():
frappe.throw("Not Permitted")

Official References (Verified v15)

Docs: https://docs.frappe.io/framework/user/en/api/page

Repo: https://github.com/frappe/frappe/tree/version-15

Folders:

frappe/desk/page/
frappe/website/router.py
frappe/desk/utils.py

Click to rate this post!
[Total: 0 Average: 0]