Developer suggestions for improved UI Grid?

Consider the following screenshot. This shows an ERPNext child DocType (a Purchase Order Item) displayed in the traditional grid layout.

As you know, this “grid widget” has a few limitations:

  1. At most, we can display 11 columns.
  2. Column widths are limited to increments of 1 “column”.
    • It scales automatically, and we cannot control this.
    • We cannot increase/decrease the size in pixels, millimeters, em/rem, etc.
  3. I don’t know an easy method to hide/remove the 1st column:
  4. I don’t know an easy method to hide/remove the final column:

My goal: To find development workarounds and/or alternatives to the Grid.

As of Frappe/ERPNext v13.6, there are no built-in alternatives.

  • The legacy Web Forms are deprecated, and no longer supported.
  • Many of us hoped that DocType Layouts would become the spiritual successor to Web Forms. However, DocType Layouts are completely broken in v13. Maybe someday they’ll be fixed. But even then, I have no confidence they’ll meet my expectations.

My question to fellow ERPNext Developers: Have any of you developed a front-end alternative solutions to the Grid, that you’re willing to share? Some kind of alternative UI widget for displaying DocFields.

Worst case, I guess we start learning Vue/React (or another frontend framework). And begin “attaching” that onto ERPNext. This is probably a good long-term goal. I know some developers like Youssef have successfully gone down this path.

But it’s a big learning curve. Not to mention, a bunch of other software to install, configure, and maintain. I would rather use Frappe itself. But customizing the frontend is not one of my strongest areas.

Bonus Points: Since we’re talking about displaying data. How could we display something that is not a DocField, but rather a calculated value?

For example, assume I have 2 DocFields named foo and bar. I want to display their sum as a “field” named baz (especially on a grid). But I don’t want to actually create a 3rd DocField, and waste SQL storage space. I just want to sum those 2 values dynamically, “on the fly”, in a grid, using functions and calculations. (don’t care if they’re JS or Python).


This limitation irritates me across many deployments!

I agree we need to pay this some attention.

And if any of us has succeeded in coming up with a solution…please share.

I like the idea of frappe datatable with horizontal and vertical scrolling enabled for the grid with editable data cell.


The most data table I like is from Prime VUE
We have used it on many front-end projects

I hope to see something similar in a frappe


I’ve been playing around quite a bit with HTML fields prepopulated with Vue templates. It’s extremely flexible and quite performant. For UI-side complex data representations, there’s nothing more powerful. So far, we’ve used it mostly for read-only data, but I suspect that it should be possible to add deeper interactivity via rest calls.

In the long run, I’d love to see a “Computed” field type. As a starting point, even just something that facilitates a single column SQL join would be huge.

To make such important changes happen we need to know how to channel all our resources such as:
0. Listing of all such critical changes that affect implementation. (There needs to be a place to record it. Can GitHub issue be the place with specific tags for each issue?)

  1. Capability (need developers listing with skillset who can be tapped)
  2. Testers (functionally capable people who can contribute their time)
  3. Finances (people who are able to source funding and are ready to share for the feature)

The bouty used to work very well for such scenario but has not been active. Last I changed it gave an error " There was an error building this page".

calculated value using this?

That’s a link to the frappejs documentation. I suspect that everyone here is asking about regular frappe.

@aakvatech To clarify. My post was not intended to propose changes to ERPNext, or suggest we champion a future solution. I’m looking for practical workarounds for what we’ve got. Developers who have solved this problem already, by creating their own custom “widget”, and are willing to share how they accomplished that.

@auliabismar @peterg
Correct, talking about regular Frappe+ERPNext.

I would also like to see the Grid UI and Web Forms with grid UI work better because it helps in other frappe apps. Not able to see more than 11 columns and that too in different sizes brings in a limitation when users have wider screens and want to see all information at a go.

I believe the Web Forms were deprecated due to a need to have employee self service user type, but I stand to be corrected.

Following up on my previous post, here’s a trivial proof of concept that shows how incredibly powerful vue templates can be in the context of frappe forms.

Step 1. In any doctype form, create a new field of type HTML and use the following template in the “Options” box:

    <table id="cat-facts" class="table table-bordered">
        <caption style="caption-side: top;">Cat Facts</caption>
            <tr class="d-flex">
                <th class="col-3">ID</th>
                <th class="col-2">Date Created</th>
                <th class="col-7">Fact</th>
            <tr v-if="facts_loading">
                <td style="text-align: center">
                    <div class="spinner-border spinner-border-sm text-secondary" role="status">
                        <span class="sr-only">Loading...</span>
            <tr v-cloak class="d-flex" v-for="(fact, index) in cat_facts">
                <td class="col-3">{{ fact._id }}</td>
                <td class="col-2">{{ fact.createdAt.slice(0,10) }}</td>
                <td class="col-7">{{ fact.text }}</td>
            <tr v-cloak v-if="cat_facts.length === 0 && !facts_loading">
                <td style="font-style: italic">No data.</td>

(This is quite a bit longer than it needs to be because it has a few niceties added, such as a spinning wheel during load and a “No data.” row that appears if the table is empty.)

Step 2. Create a new client script for whatever doctype you’re using, and add this form javascript:

frappe.ui.form.on('Offer Term', {
	refresh(frm) {
		var vm = new Vue({ 
            el: "#cat-facts", 
            data: {
                facts_loading: true,
                cat_facts: []
        frm.vm = vm;
        $.get("", (data) => {
            vm.facts_loading = false;
            vm.cat_facts = data;

Step 3. Load the doctype up and behold your creation:

It’s all pretty simple to set up. I haven’t used the component library @youssef mentioned, but it looks great and I suspect it’d be pretty straight forward to incorporate. If you need something more powerful than the built in widget library, Vue gives you almost limitless power.


Although I’m unable to repeat your demo (I’m probably missing prerequisite Vue components), the screenshot looks very cool!

Conceptually, I believe I understand what’s happening:

  • A Vue element is associated with the HTML table having id = “cat-facts”
  • On refresh, data is loaded into an Object from
    • Which also flips the boolean variable facts_loading to false, thus stopping the spinner.
  • v-for is doing a loop (similar to a Jinja/Mustache/Handlebars template language)
    • 1 row displayed per record in the ‘cat_facts’ array/list.
  • CSS to make columns the appropriate width, etc.

Unfortunately, Frontend development is currently my weakest area. The type of Frontend wizardy you just demonstrated is approaching the limits of my know-how. If I tried to take your example, and “turn it up to 11”, I would quickly get lost. For example:

  • Fetching data from DocFields, without making a new HTTP call. I’m mostly ignorant about how Doc data is being stored in the browser. I’m assuming it’s either inside some JS objects. Or there’s a way to walk the DOM and get it. Or both. :man_shrugging:
  • Adding anchors intelligently based on metadata.
  • Ability to “expand” into the full-screen details page.
  • Sort/Filter.
  • Etc.

For a few years, I’ve been meaning to learn Vue, but always get caught up in other priorities. Maybe I’ll carve out some time this winter, and finally dive into it.

For me, it’s quickly becoming a must-have skill. The out-of-the-box Frappe frontend isn’t meeting my clients’ requirements (or my own). So need new options.

Hmm…I wonder what’s missing. It should all work straight out of the box. Vue comes preinstalled in vanilla Frappe. Did you tick the “enabled?” box on the client script? I always forget to do that. Any errors in the console? Can you test that the client script is running by putting a console.log("script loaded") in the refresh event?

Your account is spot on. Frappe uses bootstrap, so there’s no need for custom css beyond that. If you notice the class="col-3" lines, those are specifying the width, etc.

Vue is a binding framework (among other things). Back in the bad old days, you had to muck around a lot with event listeners and update functions to keep the data and the presentation in sync, but Vue allows you to bind javascript variables to the DOM directly. Once you get the demo up and running, try typing cur_frm.vm.cat_facts into the console. That’s your data structure, and if you change any of the values it will immediately be reflected in the data table. (cur_frm is the standard frappe variable representing the currently active form; vm is the vue instance I created; and cat_facts is the data structure being represented in the template.)

There are a number of other frameworks similar to Vue (React, Angular, etc.), but one thing that’s nice about Vue is that it can be adopted incrementally. You can load it up and use it for just one field or function, which is ideal for use in the context of an existing framework like Frappe. Other frameworks tend to be more all or nothing.

This example is pretty trivial, but it should be possible to create a two-way binding directly to the cur_frm.doc variable, which represents all document variables. There’s no need to troll the DOM or make additional HTTP calls. For a fullscreen details page, you could use either the frappe or the boostrap modals. Filtering/sorting is all just done directly on the javascript data, which will update the view immediately. Lots of possibilities, and Vue has an extremely robust component library.

Let me know if you still can’t get the demo working after trying the steps I mentioned above. Happy to troubleshoot, and happy to walk through more details with anyone interested!


I managed make it work

the problem I encountered is that the template text as html field’s options saved to database get sanitized, the Vue attribute v-if get auto removed, so the template after save is as following

finally there is error in web console

I manually set the options via bench console to solve the issue.

so if we got to integrate Vue, we need to adjust the sanitize_html function to support Vue attribute.


You can use Vanilla JavaScript with JQuery, check this example :

Of course, the code in the previous example needs to be modified to fit your use case, and the HTML field must be used to create a place in the form to render it in, but the principle at the end, is the same.

But to create a child table with features similar to those in frappe, will take more than that, but it is possible.

Thanks for all your work @szufisher. The html sanitation is strange, though it explains the console error you’re seeing. I wonder why it’s happening to you and not to me. Hmm…I’ll poke around to see if I can figure out what’s happening. Do you know what version you’re testing on?

I think the grid should be updated to be more
like a database table! Add some colour tags over texts, easy edit mode (edit from
the grid), drop downs, etc.

I had the same problem and messages as @szufisher.

@youssef , thanks for sharing that tutorial!
Really fantastic; there’s a lot of things to learn from that. :100:

1 Like

After 15min of poking around, I can’t figure out why your HTML is getting sanitized by mine isn’t. I think I might have found a fix, though. If you look at your field in the Custom Field list (not the Customize Form page), the new field you created should have an option called “Ignore XSS Filter”. If that’s ticked, Frappe should leave all of the vue-specific attributes in place. If you try it, let us know if it works.