Material-table: Sticky headers

Created on 2 Jul 2019  路  12Comments  路  Source: mbrn/material-table

Is your feature request related to a problem? Please describe.
Yes.

Describe the solution you'd like
Headers to be sticky.

Describe alternatives you've considered
Finding a way to override styles

Additional context

Seems like the <ScrollBar/>-component which adds a wrapper with overflow, makes it impossible.

Not sure why #709 was closed.

feature help wanted wontfix

Most helpful comment

The reason I closed it is because there is a very easy solution that I found. First you need to set the header style to sticky:

options={{ headerStyle: { position: 'sticky', top: 0 } }}

This alone does not do anything. Next you need to set the body height of the table in order to trigger a vertical scrollbar to appear:

options={{ headerStyle: { position: 'sticky', top: 0 }, maxBodyHeight: '650px' }}

The combination of these two function will allow you to have sticky table headers. Hope this helps. Perhaps it should be added to the docs if this is an acceptable solution

All 12 comments

The reason I closed it is because there is a very easy solution that I found. First you need to set the header style to sticky:

options={{ headerStyle: { position: 'sticky', top: 0 } }}

This alone does not do anything. Next you need to set the body height of the table in order to trigger a vertical scrollbar to appear:

options={{ headerStyle: { position: 'sticky', top: 0 }, maxBodyHeight: '650px' }}

The combination of these two function will allow you to have sticky table headers. Hope this helps. Perhaps it should be added to the docs if this is an acceptable solution

Is there any way to do this without setting the maxBodyHeight ?

I have a table (without pagination) in a view, where I want a sticky header.
But when setting maxBodyHeight this creates a new scrollbar within the table. I want the main scrollbar to do the scrolling.

Is this possible at the moment? Any tips?

@karrettgelley
Any tip to stick also the filter row (depending of the height of the header row)?
The dividers (on top and bottom of the filter row) seem to no be impact by the 'sticky' option..

@simromm
Any tip to stick also the filter row (depending of the height of the header row)?
The dividers (on top and bottom of the filter row) seem to no be impact by the 'sticky' option..

I've looked into this and it seems impossible with this project's architecture. The filter row is in the table body rather than the header. I need this for my current project, so my plan is to build a custom header where each cell is a div with the label and a filter.

I got sticky filter row to work, but I had to fork the project to do it.

* WARNING: UGLY HACK AHEAD *

The key was that col.title doesn't have to be a string. Map this for each column:

addFilterToCol(col) {
    if ('string' !== typeof col.title) return;
    const table = this.table; // This is a MaterialTable ref
    const HeaderCell = () =>
        <TextField
            type='search'
            defaultValue={col.tableData.filterValue || ''}
            placeholder={col.filterPlaceholder || ''}
            style={col.filterCellStyle}
            onClick={(event) => event.stopPropagation()}
            onChange={(event) => {
                const callback = _.get(table.current, 'onFilterChange');
                if (callback) callback(col.tableData.id, event.target.value);
            }}
        />;
    HeaderCell.bind(this);
    Object.assign(col, { textTitle: col.title, title: <>{col.title}<br/><HeaderCell/></> });
}

Note that textTitle was so that in my material-table fork I could display col.textTitle || col.title in the show/hide columns menu. You can leave that out, but you will get filter TextFields in the show/hide columns menu because it uses the now hijacked column title.

Use at your own risk! This is unintended, unsupported functionality!

Use at your own risk! This is unintended, unsupported functionality!

Would you be willing to make this a bit less of an "ugly hack" and make a pull request out of it?

I think I and a lot of others would love this kind of feature

Would you be willing to make this a bit less of an "ugly hack" and make a pull request out of it?

I think I and a lot of others would love this kind of feature

I hear you. The trouble is to fix this the "right" way would be an architecture-level change for the project, so we would need @mbrn to weigh in. I looked first at just moving the filter row to the header, but that is very difficult as well because making two separate header rows sticky is very difficult in general. It needs to be one row where each cell has a div with the title and the filter input. I've "hacked" this by making new dummy inputs and then fowarding the event handling to the existing filter row while keeping that row hidden. I don't think that's something that would get approved as a PR.

I'll ask my employer for permission to publish my base table that "has a" material-table that sorts most of these issues out along with a material-table fork with my PRs that make this hack work.

@ingvaldlorentzen any solutions yo came up with, to use the main scroll ? like without the maxHeight ?

@ingvaldlorentzen any solutions yo came up with, to use the main scroll ? like without the maxHeight ?

No, not yet I'm afraid. If I do, I'll post any updates here!

<MaterialTable
                    components={{
                        Container: props => (
                            <div style={{height: "60vh"}}> //same as maxBodyHeight
                                {props.children}
                            </div>
                        ),
                        Toolbar: props => null
                    }}
                    title={""}
                    columns={headers}
                    data={data}
                    parentChildData={(row, rows) =>
                        rows.find(a => a.Identifier === row.Parent)
                    }
                    options={{
                        search: false,
                        paging: false,
                        sorting: false,
                        draggable: false,
                        headerStyle: {position: "sticky", top: 0},
                        maxBodyHeight: "60vh"
                    }}
                >
</MaterialTable>

In my case making the height of the container(using the component prop) and maxBodyHeight same fixed it. I'm using the tree data, but I think it should work in other cases.

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions. You can reopen it if it required.

I really feel like the filter row should be part of the header. We are also trying to make it sticky but no luck because of it's DOM position.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

Likurg2010 picture Likurg2010  路  3Comments

VipinJoshi picture VipinJoshi  路  3Comments

ModPhoenix picture ModPhoenix  路  3Comments

KKrisu picture KKrisu  路  3Comments

revskill10 picture revskill10  路  3Comments