Markup & Template Structure
TableCrafter renders tables without overridable PHP partials. Instead it emits a predictable, class-driven HTML skeleton from the [tablecrafter] shortcode and progressively enhances it in the browser, so you customize the output by targeting its stable CSS classes and data attributes.
How rendering works
There is one shortcode, [tablecrafter], registered in tablecrafter.php and bound to the render_table() method. The Gutenberg block and the Elementor widget both funnel into this same method, so every integration produces identical markup.
Rendering happens in two phases:
- Server-side (SSR): On first render PHP fetches the source, builds a crawlable
<table class="tc-table">, caches the HTML in a transient, and prints it inside the container. The same HTML is reused on subsequent loads via a stale-while-revalidate transient cache. - Client-side (hydration):
frontend.jsfinds every.tablecrafter-container, reads itsdata-*attributes, and instantiates theTableCrafterclass fromtablecrafter.js. It wraps the SSR table in a.tc-wrapperand injects the interactive toolbar, pagination, and refresh UI around it.
TableCrafter ships no theme-overridable template files (no templates/ folder, no locate_template() lookup). Customization is done through CSS classes and the JS event API documented below, not by copying partials into your theme.
The outer container
The shortcode always outputs a single wrapper <div> whose data-* attributes carry every shortcode setting forward to the JavaScript. This is the integration surface for both styling and scripting.
<div id="tc-block-1a2b3c" class="tablecrafter-container"
data-source="https://example.com/data.json"
data-include="" data-exclude="" data-root=""
data-search="true" data-filters="true" data-export="false"
data-per-page="25" data-sort="price:desc"
data-auto-refresh="false" data-refresh-interval="300000"
data-ssr="true">
<!-- server-rendered <table> goes here -->
<script type="application/json" class="tc-initial-data">[...]</script>
</div>
Two details worth noting: the initial dataset is serialized into an inline <script type="application/json" class="tc-initial-data"> tag so the JS can hydrate without a second network request, and data-ssr="true" tells the JS that server markup already exists and should be wrapped rather than replaced.
Container data attributes
These attributes mirror the shortcode attributes one-to-one. Override them by editing the shortcode; the JS reads them at init time.
| Attribute | Source attr | Purpose |
|---|---|---|
| data-source | source | JSON/CSV/Google Sheet URL. Required for any output. Required |
| data-search | search | Renders the .tc-global-search-container input. Optional |
| data-filters | filters | Renders the .tc-filters per-column controls. Optional |
| data-export | export | Renders .tc-export-controls (CSV/XLSX/PDF + copy). Optional |
| data-per-page | per_page | Page size; 0 disables pagination. Optional |
| data-sort | sort | column:direction initial sort, applied server-side. Optional |
| data-include / data-exclude | include / exclude | Comma-separated column allow/deny lists. Optional |
| data-root | root | Dot-path to the array inside a nested JSON response. Optional |
| data-auto-refresh / data-refresh-interval | auto_refresh / refresh_interval | Enables the live .tc-refresh-indicator widget. Optional |
Server-rendered table markup
The fetch_and_render_php() method produces semantic, accessible table HTML. Headers carry sorting hooks (tabindex, aria-sort, data-field) and each cell carries a data-tc-label attribute mirroring its column header, which the JS uses when collapsing to mobile cards.
<table class="tc-table">
<thead><tr>
<th class="tc-sortable" tabindex="0"
aria-sort="descending" data-field="price">Price</th>
</tr></thead>
<tbody>
<tr>
<td data-tc-label="Price">$42.00</td>
</tr>
</tbody>
</table>
All server-generated HTML is passed through sanitize_table_html() (a wp_kses allow-list) before printing, so any custom markup must use the supported tags: table, thead, tbody, tr, th, td, a, img, span, time, div, strong, em, small with their whitelisted attributes only.
Smart cell value formatting
Rather than printing raw strings, render_value_php() detects the value type and wraps it in a recognizable element you can target with CSS:
| Detected type | Output markup |
|---|---|
| Boolean true/false | <span class="tc-badge tc-yes">Yes</span> / tc-no |
| Image URL | <img loading="lazy"> (max 100px wide) |
<a href="mailto:…"> | |
| ISO date | <time datetime="…"> |
| HTTP(S) URL | <a target="_blank" rel="noopener noreferrer"> (truncated text) |
| Array of values | <div class="tc-tag-list"> of <span class="tc-tag">, with tc-tag tc-more overflow chip |
To restyle "Yes/No" pills, link colors, or tag chips, target .tc-badge, .tc-table a, and .tc-tag in your theme CSS. These classes are stable across both the SSR table and the client-rebuilt table.
Client-built toolbar & controls
During hydration the JS injects interactive controls into the .tc-wrapper in a fixed order. Each is a discrete, classed block, so you can show, hide, or reposition them with CSS:
.tc-global-search-container→ holdsinput.tc-global-search(debounced,aria-label="Search table")..tc-filters→ an optionalbutton.tc-clear-filtersplus a.tc-filters-rowof.tc-filterblocks. Each filter contains a.tc-filter-labeland a control (.tc-filter-input, multiselect, date-range, or number-range based on detected column type)..tc-export-controls→ either a singlebutton.tc-export-csvor, when multiple formats are enabled, a.tc-export-dropdown-wrapper>button.tc-export-main-btn+.tc-export-dropdownof.tc-export-option.tc-export-{csv|xlsx|pdf}buttons. Always followed bybutton.tc-copy-clipboard..tc-pagination→.tc-pagination-infoplus.tc-pagination-controlscontaining.tc-prev-btn,.tc-next-btn, and (for large datasets).tc-first-btnand a.tc-page-jumpwithinput.tc-page-input.
Responsive card view
On narrow viewports the JS swaps the table for a card layout instead of horizontally scrolling. The structure is .tc-cards-container[role="list"] > .tc-card[role="listitem"], each with a .tc-card-header, a .tc-card-body of .tc-card-field rows (.tc-card-label + .tc-card-value), and an optional .tc-card-toggle expander (.tc-card-expandable / .tc-card-expanded) for fields hidden at the current breakpoint.
Auto-refresh indicator
When auto_refresh is enabled the JS appends a .tc-refresh-indicator widget containing .tc-refresh-status (.tc-refresh-icon, .tc-refresh-text, optional .tc-countdown and .tc-last-updated spans) plus button.tc-refresh-toggle and button.tc-refresh-manual. The paused state adds a .paused class to the indicator.
Theming with CSS
The stylesheet (assets/css/tablecrafter.css, enqueued as handle tablecrafter-style) uses hard-coded color values rather than a public set of CSS custom properties, so theme overrides are done by targeting classes. The one place variables appear is a high-contrast mode: adding .tc-high-contrast to .tc-wrapper activates the --tc-border-color, --tc-text-color, --tc-bg-color, and --tc-focus-color variables.
/* Recolor headers and the sortable cursor */
.tc-table th { background-color: #0f172a; color: #fff; }
.tc-table th.tc-sortable:hover { background-color: #1e293b; }
/* Hide the copy-to-clipboard button but keep export */
.tablecrafter-container .tc-copy-clipboard { display: none; }
Scope overrides to .tablecrafter-container (or a wrapping element). The tc-* classes are shared by every table on the page, so unscoped rules affect all of them.
JavaScript events
For card interactions the instance dispatches bubbling CustomEvents on the container, namespaced under tablecrafter:. Listen on .tablecrafter-container to react without forking the plugin.
const el = document.querySelector('.tablecrafter-container');
el.addEventListener('tablecrafter:cardTap', (e) => {
console.log(e.detail.rowData, e.detail.rowIndex);
});
// Also available: tablecrafter:cardView, tablecrafter:cardEdit
Generating a shortcode
You rarely hand-write these attributes. The admin screen at WP Admin → TableCrafter (menu slug tablecrafter-wp-data-tables) provides a live-preview playground and a copy-ready shortcode builder. A typical generated tag looks like:
[tablecrafter source="https://example.com/products.json" search="true" filters="true" export="true" per_page="25" sort="price:desc"]
Next steps: see shortcode-attributes.html for the complete attribute reference, and styling-and-theming.html for a full class-by-class CSS targeting guide.