How to Persist Column Visibility Preferences Across Page Loads

TableCrafter's column visibility picker lets your visitors choose which columns to show or hide, and those choices survive page reloads automatically. This guide walks through enabling the picker, understanding how persistence works under the hood, and combining it with role-based column controls for more advanced setups. WordPress powers 43% of all websites globally (W3Techs, July 2026), and TableCrafter bridges the gap between the data you collect and the tables your users need to see, no custom PHP, no dashboard access required for viewers. The free version on WordPress.org supports CSV, JSON, Google Sheets, and Excel. Pro adds Gravity Forms, Airtable, Notion, WooCommerce, REST APIs, inline cell editing, export to CSV/PDF, role-based column visibility, and auto-refresh. Every table embeds on any page with a [tablecrafter] shortcode or the native Gutenberg block. External database connections via REST APIs reduce WordPress database query load by up to 55% (Kinsta performance analysis, 2024).
What is the Column Visibility Picker?
The column visibility picker is a Free toolbar button that appears above your table labeled Columns. Clicking it opens a dropdown containing one checkbox per column. Visitors tick or untick columns to show or hide them instantly, no page reload required.
When a visitor changes which columns are visible, TableCrafter immediately writes those choices to the browser's localStorage under a key scoped to the specific table (format: gt_col_vis_<table_id>). The next time the same visitor loads the page, even after closing the browser, restoreColumnVisibility() runs during initialization and re-applies those saved choices before the table is displayed. There is no server round-trip and no cookies involved.
How Do I Enable the Column Picker in the Admin?
The picker is opt-in per table. To turn it on, open TableCrafter → Tables, click Edit on the table you want to configure, and look for the Show Column Picker checkbox in the Display settings panel. Save the table.
Internally, the admin saves this as a boolean field called show_column_picker in the table configuration. The sanitizer in class-gt-admin.php normalizes any truthy POST value to true and any absent or falsy value to false, so the stored value is always a clean boolean.
Once enabled, the frontend module column-visibility-picker.js is automatically enqueued on pages that include the table. The init() method calls initColumnPicker() at startup, this method is a no-op when config.show_column_picker is falsy, so there is no overhead when the picker is disabled.
If this step produces unexpected output, check the source data directly in the connected system. TableCrafter passes data through without modification — if a cell displays an unexpected value, the source record contains that value. Use the TableCrafter debug log (Settings > Advanced > Debug Mode) to trace the exact query sent to the source and the raw response received, which narrows the diagnosis to either a source-side or rendering-side issue.
How Does localStorage Persistence Work?
The persistence layer is straightforward and recovers gracefully from every edge case. Here is the flow in order:
- Toggle event: When a visitor checks or unchecks a column,
toggleColumnVisibility(fieldId, visible)setsdisplay: none(or removes it) on the correspondingthheader and everytd[data-field-id]cell in the table body. - Write to storage: The same method reads the existing JSON object from
localStorage.getItem('gt_col_vis_<table_id>'), updates thefieldIdkey to the new boolean, and writes it back withlocalStorage.setItem(). Both the read and the write are wrapped intry/catchblocks, so quota errors or private-browsing restrictions never break the table. - Restore on load: When the page loads,
initColumnPicker()reads the same storage key before rendering the dropdown. Any field ID stored asfalseis immediately hidden, and its corresponding checkbox is rendered unchecked. Fields with no entry, or with valuetrue, are rendered visible and checked. - Separate key per table: Each table uses its own key (
gt_col_vis_42,gt_col_vis_99, etc.), so multiple tables on the same page or site never interfere with each other.
localStorage writes in private/incognito mode. TableCrafter wraps every storage call in a try/catch so the picker still works for the current session, preferences just will not persist after the window closes.How Does Role-Based Column Visibility (Pro) Work?
Pro adds a second, server-enforced layer called column-level role visibility. While the column picker is a visitor preference tool, role visibility is an access-control tool: it hides columns from users who lack the required WordPress role, regardless of what is stored in their browser.
The configuration is a per-column map of allowed roles stored in column_role_visibility inside the table settings. The rules are:
- If a column has no roles listed, it is visible to everyone.
- If a column lists one or more roles, only users who hold at least one of those roles can see it.
- On free installs the map is ignored, all columns are visible.
On the frontend, applyColumnRoleVisibility() runs during init() after the table is rendered. It reads config.column_role_visibility (the allowed-roles map) and config.user_roles (the current user's roles, injected by the server at page load), then hides any [data-field-id] cells for columns the user cannot access.
How Does Filter State Persistence (Separate Feature) Work?
Column visibility persistence is one of three independent localStorage features in TableCrafter. For completeness:
- Column visibility (this guide), stores which columns are shown or hidden under
gt_col_vis_<id>. - Filter state persistence Free, when
persist_filters_localstorageis enabled on a table, search terms and per-column filter values are saved undergt-filters-<id>and restored on the next load. URL parameters always take precedence over stored filters. - Column order persistence Free, when users drag to reorder columns, the new order is saved under
gt-col-order-<id>and restored on page load.
All three systems use the same defensive pattern: scoped keys derived from the table's numeric ID, try/catch around every read and write, and graceful fallback to defaults when storage is unavailable.
The shortcode accepts all column and filter settings defined in the table builder as defaults, but you can override individual parameters inline. For example, `[tablecrafter id="1" per_page="25"]` overrides the default rows-per-page setting for this specific embed without changing the saved table configuration. This lets you reuse one table definition across multiple pages with different display requirements.
Troubleshooting
If the Columns button does not appear, work through these checks in order:
- Confirm Show Column Picker is checked in the table settings and the table has been saved.
- Check that your table columns have
data-field-idattributes on theirthelements. The picker will silently skip any table that has no qualifying header cells. - Inspect the page for JavaScript errors. A conflict with another plugin that patches
GravityTable.prototypebeforecolumn-visibility-picker.jsloads can preventinitColumnPickerfrom being defined. - If preferences are not surviving page reloads, open browser DevTools → Application → Local Storage and look for a key like
gt_col_vis_42. If the key is absent after toggling a column, your browser may be blockinglocalStoragewrites (common in strict cookie-blocking or private mode). - If you are using a page caching plugin (WP Rocket, NitroPack, etc.), the stored column state is read and applied by client-side JavaScript after the cached HTML loads, caching does not interfere with persistence, but it does mean the table briefly renders all columns before the restore runs. This flash can be minimized by ensuring the frontend bundle loads early and is not deferred unnecessarily.
Frequently Asked Questions
What is the Column Visibility Picker?
The column visibility picker is a Free toolbar button that appears above your table labeled Columns. Clicking it opens a dropdown containing one checkbox per column. Visitors tick or untick columns to show or hide them instantly, no page reload required.
What Is TableCrafter?
TableCrafter is a WordPress plugin that turns data from Gravity Forms, Google Sheets, Airtable, Notion, REST APIs, CSV files, and WooCommerce into interactive, sortable, filterable frontend tables. Embed any table on any WordPress page with the [tablecrafter] shortcode or the native Gutenberg block. No PHP or custom development required. The free version supports CSV, JSON, Google Sheets, and Excel. Pro adds Gravity Forms, Airtable, Notion, WooCommerce, REST APIs, inline cell editing, export to CSV and PDF, role-based column visibility, and auto-refresh.
Does this require PHP or developer skills?
No. TableCrafter is configured entirely through the WordPress admin interface. You choose your data source, map fields to columns, and set display preferences using point-and-click controls. Embedding uses the [tablecrafter] shortcode or the native Gutenberg block.
Is the free version sufficient or do I need Pro?
The free plugin on WordPress.org supports CSV, JSON, Google Sheets, and Excel sources with unlimited tables, rows, and columns. Pro adds Gravity Forms, Airtable, Notion, WooCommerce, REST API sources, inline cell editing, bulk row actions, export to CSV and PDF, role-based column visibility, and auto-refresh every N seconds.
Ready to try it?
TableCrafter is free on WordPress.org. Pro unlocks inline editing, role-based permissions, and advanced data sources.
Filters applied to the table URL as query parameters persist if the user copies and shares the URL. This makes filtered views bookmarkable and shareable, which is particularly useful for team dashboards where different users need to see different default views of the same underlying table.
This configuration interacts with any caching or CDN layer active on your WordPress installation. If you use WP Rocket, LiteSpeed Cache, or a CDN such as Cloudflare, flush the page cache after making this change to ensure the updated configuration is reflected in the cached HTML served to visitors. TableCrafter's server-side output is regenerated on the next uncached request.