How to Hide Table Columns Based on WordPress User Role

Updated July 2026 • 6 min read • By Fahad Murtaza

TableCrafter table builder, connect Gravity Forms, Google Sheets, Airtable, CSV, or JSON data sources
TableCrafter table builder, connect Gravity Forms, Google Sheets, Airtable, CSV, or JSON data sources

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, Excel, JSON, and Save as PDF, role-based column visibility, and auto-refresh. Every table embeds on any page with a [tablecrafter] shortcode or 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:

Wholesale Price, Cost, and Margin must be completely invisible to Customers, absent from the page source, not just visually hidden.

How Do I Configure Column-Level Visibility?

Open the table in TableCrafter → Tables and expand the 2. Select & Configure Fields collapsible section. Each column appears as a row in a sortable list. Expand the Wholesale Price column by clicking its row.

Locate the Visible to roles (Pro) checkboxes. Each registered WordPress role appears with a checkbox. 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 for new page loads. If you are running a full-page caching plugin like NitroPack or WP Rocket, clear the cache for the relevant page so visitors get the updated response.

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 2. Select & Configure Fields section.

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.

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:

  1. DevTools bypass: Any user can open browser DevTools, select the hidden element, and remove the display: none rule. The data is visible instantly.
  2. 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.
  3. 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.

If your hidden columns contain financial data, personal information, or competitive intelligence, CSS-only hiding is a data exposure risk. Configure Visible to Roles in the column settings to enforce hiding at the server level.

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, column visibility settings for that user are irrelevant because the table never renders for them. The two controls form a two-layer gate: table access is the outer gate, column visibility is the inner gate.

Configure table-level access in the User Permissions group inside 3. Table Features. The Table Access Control multi-select lists all registered WordPress roles. Select only the roles you want to have access. Leave the list empty to allow all users (including unauthenticated visitors) to view the table.

A practical three-tier setup: the Customer role is selected in Table Access Control (table visible, five public columns shown). The Shop Manager role is also selected (table visible, all eight columns shown via individual column settings). The Subscriber role is not selected (table renders nothing for Subscribers, they see whatever the page template shows in place of the shortcode). The Administrator role always has full access regardless of the Table Access Control setting.

When a user holds multiple WordPress roles simultaneously (which WooCommerce sometimes produces), TableCrafter resolves visibility by showing the union of what each role can see. A user who is both Customer and Shop Manager sees all eight columns because Shop Manager's visibility includes all columns. Design your role structure with this union behavior in mind rather than assuming restriction always wins.

How Does Handling Exports with Hidden Columns Work?

When bulk export is enabled in the Bulk Actions group, the CSV, Excel, or JSON 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.

[tablecrafter id="7"]

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 runs in TableCrafter's server-side response layer, implemented in the TC_Column_Visibility class, which operates on the column configuration rather than on the raw source data. The source always returns all fields it has; TableCrafter's permission layer strips non-visible columns from the payload before it leaves the server, regardless of which connector fetched the data.

This architecture has a practical implication: you do not need to configure visibility separately in each source system. You do not need to create a filtered Google Sheet view for each role, or a restricted Airtable view per role tier, or a scoped REST API endpoint per role. You configure visibility once in the TableCrafter column settings and it applies to all roles across all source types.

One edge case to be aware of: if your data source is a REST API that returns paginated data, and the sensitive columns are at the end of each row object, TableCrafter still strips them server-side before the frontend ever receives the response. However, if the REST API itself returns different fields for different auth tokens, configure the API connector to use the broadest token (admin-level) so TableCrafter always receives the full dataset and its own permission layer handles the scoping. Using a narrow token and relying on the API to hide fields is an extra dependency that can cause "column not found" errors if the API changes its response shape.

What Are the Next Steps?

Column visibility and table-level access together cover most role-based use cases, but two more layers extend the model further.

Inline editing permissions let you restrict which roles can edit a column even if they can see it. A Sales Rep role might see the Wholesale Price column because their manager wants them aware of margins, but they should not be able to change it. Configure this in 2. Select & Configure Fields for each column: separate from the visibility checkboxes, you can set which roles can write to the column when frontend editing is enabled.

URL parameter filters let you scope which rows a user sees without changing which columns they see. A filtered table URL like /loads/?region=north automatically applies a row-level filter on page load. Combine that with column visibility and a given user sees only their region's rows and only their permitted columns, all from a single table configuration.

After configuring column visibility, verify by logging in as the restricted role in an incognito window and checking both the rendered table and the Network tab's AJAX response in browser DevTools. Confirm the sensitive column keys are absent from the JSON payload entirely. If they appear in the response even with no cells visible on screen, there is likely a caching layer serving a stale admin-generated response. Flush your page and object caches and retry.

For the full cross-source behavior, see the guide on how column role visibility works across data sources, which walks through Gravity Forms, WooCommerce, and REST API examples side by side.

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, Excel, JSON, and Save as 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, Excel, JSON, and Save as 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.