How to Hide Table Columns Based on WordPress User Role

Column-level role visibility lets you serve one table to multiple user types, showing pricing to admins, hiding it from customers, without maintaining separate tables or writing custom PHP. This guide walks through the configuration, explains what the server output looks like for each role, and covers why CSS-only hiding creates real security problems. 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, and no per-row limits on the free tier. 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. Notion databases support up to 10,000 rows per database on free plans (Notion documentation, 2025).
What Is the Example Scenario?
A WooCommerce product catalog table has eight columns: SKU, Product Name, Category, Stock Level, Retail Price, Wholesale Price, Cost, and Margin. The requirements:
- Customers see: SKU, Product Name, Category, Stock Level, Retail Price.
- Shop Managers see all columns.
- Administrators see all columns plus an edit control.
Wholesale Price, Cost, and Margin must be completely invisible to Customers, absent from the page source, not just visually hidden.
This setting persists across table rebuilds. If you change the data source later, you may need to revisit this step to remap columns from the new source to the existing table configuration.
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.
How Do I Configure Column-Level Visibility?
Open the table in TableCrafter → Tables and click the Columns tab. Each column appears as a row in a sortable list. Expand the Wholesale Price column by clicking its row.
Locate the Visible to Roles multi-select. It lists every role currently checked in the table's Allowed Viewer Roles list. Uncheck Customer. Leave Shop Manager and Administrator checked. Click the collapse arrow and repeat for Cost and Margin columns.
Save the table. The configuration is now active immediately, no cache clearing required for new page loads (though if you are running a full-page caching plugin like NitroPack, clear the cache for the relevant page).
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.
How Does Testing the Configuration Work?
Log in as a Customer-role user (WooCommerce assigns this role to registered store accounts). Navigate to the page with the table shortcode. You should see a five-column table: SKU, Product Name, Category, Stock Level, Retail Price.
Log out and log in as a Shop Manager. Navigate to the same URL. You should see all eight columns. The column order follows the order set in the TableCrafter Columns tab.
TableCrafter validates this configuration on save. If validation fails, the admin panel displays a specific error message identifying which field caused the problem. Correct the field value and save again without needing to restart the setup process.
After completing this step, verify the result by viewing the page as a logged-out visitor in an incognito window. This confirms the table behaves correctly for public visitors rather than reflecting admin-level permissions that may hide configuration issues during initial setup. Check both the rendered output and the browser console for any JavaScript errors.
What the Server Actually Sends?
This is the important part. TableCrafter evaluates the current user's roles before building the AJAX response. Columns the user should not see are excluded from the JSON payload entirely. The Customer-role user's browser receives:
{
"columns": [
{"key": "sku", "label": "SKU"},
{"key": "product_name", "label": "Product Name"},
{"key": "category", "label": "Category"},
{"key": "stock_level", "label": "Stock Level"},
{"key": "retail_price", "label": "Retail Price"}
],
"rows": [
{"sku": "WDG-001", "product_name": "Widget A", "category": "Widgets", "stock_level": 142, "retail_price": "$24.99"}
]
}
The Shop Manager's browser receives the same structure but with eight entries in the columns array and the additional fields present in every row object. There is no wholesale price or cost value anywhere in the Customer's response, not hidden, not null, simply not present.
The column mapping you define here is stored as a JSON configuration in the WordPress database. You can export this configuration using the TableCrafter export tool and import it to another table or another site. This is useful when replicating a table layout across multiple pages or when migrating a table to a staging environment for testing before going live.
Why CSS-Only Hiding Is Not Sufficient?
A common alternative to server-side column visibility is rendering all columns and hiding sensitive ones with CSS:
/* Do not use this as your only protection */
.role-customer .column-wholesale-price,
.role-customer .column-cost,
.role-customer .column-margin {
display: none;
}
This approach has three serious problems:
- DevTools bypass: Any user can open browser DevTools, select the hidden element, and remove the
display: nonerule. The data is visible instantly. - Page source exposure: Even without DevTools, View Source shows the hidden column data in the HTML. A technically unsophisticated user can read it by searching the page source.
- API response exposure: If the table loads data via AJAX (which TableCrafter does), the raw JSON response in the Network tab contains the sensitive column values regardless of CSS rules.
CSS-only hiding is appropriate only as a UI fallback after server-side visibility is already configured. It adds no meaningful security on its own.
How Does Combining Column Visibility with Table-Level Role Access Work?
Column visibility works within the set of users who can already see the table. If a user cannot see the table at all (their role is not in the Allowed Viewer Roles list), column visibility settings are irrelevant, they never reach the table data. Use table-level access to control the gate, and column-level visibility to control what passes through the gate for different role tiers.
This step is required before the table can render data. Skipping it or entering incorrect values will result in a connection error when the table first loads. Double-check the value by pasting it directly into the field rather than typing it manually to avoid whitespace or encoding issues.
TableCrafter re-fetches this data on each page load by default. If your data source updates infrequently and your site has significant traffic, enable the built-in caching option in the table's Performance tab. This stores the fetched data for a configurable number of minutes and serves it from WordPress transients, reducing API calls to the source and improving page load time for visitors.
How Does Handling Exports with Hidden Columns Work?
When export="true" is in the shortcode, the CSV or spreadsheet export respects column visibility. A Customer downloading the table gets a file with five columns. Wholesale Price, Cost, and Margin are absent from the file entirely, they are not in hidden columns in the spreadsheet; they simply do not exist in the export.
If the result does not match expectations after saving, use the TableCrafter debug log (enable via TableCrafter Settings > Advanced > Debug Mode) to trace exactly which configuration value is being applied for the current request.
The configuration you set here applies to every visitor who loads a page containing this table, regardless of whether they are logged in. Role-specific overrides for columns and rows are a separate layer and do not replace these global display settings. Apply global settings first, then add role restrictions as needed for tables that serve multiple user types.
[tablecrafter id="7" filter="true" search="true" export="true"]
How Does Column Visibility Across Data Sources Work?
Column role visibility works identically regardless of whether the table source is Gravity Forms, WooCommerce, a REST API endpoint, Airtable, or Google Sheets. The visibility check is applied to the column configuration in TableCrafter, not to the data source itself. The data source returns all data; TableCrafter's permission layer strips columns before the response leaves the server.
Test this step while logged in as a user with the target role to confirm the expected behavior. Logged-in admin users always see all columns and all rows regardless of role restrictions, which can mask visibility issues during initial configuration.
This step completes the connection between your data source and the TableCrafter table engine. Once saved, the plugin caches the connection credentials in the WordPress options table and uses them on every subsequent page load. If you update the source configuration later — for example, rotating an API key or changing a sheet URL — return to this step, enter the new value, and save again. The table updates immediately on next load without any shortcode changes.
What Are the Next Steps?
To understand how column visibility behaves consistently across different data sources in practice, see the guide on how column role visibility works across data sources. It walks through three concrete examples, Gravity Forms, WooCommerce, and a REST API, showing the same configuration applied to each.
The setting is stored in the WordPress options table under the table's configuration key. It does not modify the original data source and can be changed at any time without affecting the underlying records.
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.
Frequently Asked Questions
What Is the Example Scenario?
A WooCommerce product catalog table has eight columns: SKU, Product Name, Category, Stock Level, Retail Price, Wholesale Price, Cost, and Margin. The requirements:
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.
Changes take effect immediately after saving. No cache flush or page refresh is required for the new configuration to apply to all shortcode instances of this table.
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.