Rendering in PHP Templates

TableCrafter has no dedicated public PHP API — the supported way to render a table from a theme file is to pass its [tablecrafter] shortcode through WordPress core's do_shortcode(). This page documents that path end to end, using only the real shortcode tag, attributes, and markup the plugin emits.

do_shortcode() Theme Templates Server-Side Render SWR Cache Block & Elementor Parity

When to render from PHP

Most users embed tables with the editor: the [tablecrafter] shortcode, the TableCrafter → Data Table Gutenberg block (registered as tablecrafter/data-table), or the Elementor widget. Render from PHP instead when the table lives in template logic the editor can't reach:

ℹ️

The Gutenberg block callback (render_block_callback()) and the Elementor widget both build a [tablecrafter ...] string and call do_shortcode() internally. Calling it yourself in PHP is the exact same code path — there is no separate or "lower-level" render function to call.

The core pattern

The shortcode callback render_table() is not part of a documented public class API, so do not call it directly. Always go through do_shortcode() so WordPress resolves the registered tablecrafter tag and the plugin's normalization, caching, and asset-enqueue logic all run:

echo do_shortcode( '[tablecrafter source="https://example.com/api/products.json"]' );

That single call:

  1. Validates source with esc_url_raw() and renders an empty-source placeholder if it is blank.
  2. Checks the Stale-While-Revalidate transient (tc_html_…) and, on a cold cache, fetches and renders the table server-side so the HTML is crawlable.
  3. Enqueues the tablecrafter-frontend script and tablecrafter-style stylesheet.
  4. Returns a <div class="tablecrafter-container" data-ssr="true"> wrapper that the bundled JS hydrates into an interactive table.

Shortcode attribute reference

These are the only attributes the shortcode recognizes (from the shortcode_atts() defaults in render_table()). The data source attribute is source — there is no url attribute. Boolean attributes accept true, 1, or yes as true; anything else is false.

AttributeTypeDefaultDescription
sourceRequired''URL to the JSON/CSV data source. Passed through esc_url_raw(). If empty, an admin placeholder is returned instead of a table.
idOptionaltc-{uniqid}DOM id for the container. Set a stable value if you need to target it from custom JS or CSS.
includeOptional''Comma-separated list of columns to keep (allowlist). Omit to show all columns.
excludeOptional''Comma-separated list of columns to drop (denylist).
rootOptional''Path to the array inside a nested JSON response (for example data.results) that holds the rows.
searchOptionalfalseEnable the global search box.
filtersOptionaltrueEnable per-column filters. This one defaults to on.
exportOptionalfalseShow export controls (CSV, XLSX, PDF).
per_pageOptional0Rows per page. 0 disables pagination; any value > 0 enables it.
sortOptional''Initial sort as column:direction, e.g. price:desc.
auto_refreshOptionalfalsePeriodically re-fetch the source in the browser.
refresh_intervalOptional300000Auto-refresh interval in milliseconds (default 5 minutes).
refresh_indicatorOptionaltrueShow the live refresh indicator.
refresh_countdownOptionalfalseShow a countdown to the next refresh.
refresh_last_updatedOptionaltrueShow the "last updated" timestamp.

A complete template example

This renders a paginated, searchable, exportable product table from a per-post source URL. Because attribute values come from PHP, sanitize them and build the shortcode string with sprintf() rather than hand-concatenating untrusted input:

// In a custom page template or template part.
$source = esc_url_raw( get_post_meta( get_the_ID(), 'data_feed_url', true ) );

if ( $source ) {
    $shortcode = sprintf(
        '[tablecrafter source="%s" search="true" export="true" per_page="%d" sort="%s"]',
        esc_attr( $source ),
        25,
        'name:asc'
    );

    // do_shortcode() output is already sanitized server-side; safe to echo.
    echo do_shortcode( $shortcode );
}
⚠️

Build the attribute string yourself — do not interpolate raw user input straight into the shortcode. The plugin escapes its own output, but a malformed source or unescaped quote in an attribute can break shortcode parsing. Pass URLs through esc_url_raw() and other values through esc_attr() first, as shown above.

What the markup looks like

The shortcode returns a single wrapper whose data-* attributes mirror the attributes you passed. On a warm cache it already contains the server-rendered .tc-table; on a cold first hit it ships a .tc-loading placeholder plus an inline JSON island the JS hydrates from:

<div id="tc-..." class="tablecrafter-container"
     data-source="..." data-search="true" data-export="true"
     data-per-page="25" data-sort="name:asc" data-ssr="true">
  <!-- server-rendered .tc-table, or .tc-loading on cold cache -->
  <script type="application/json" class="tc-initial-data">[...]</script>
</div>

Useful container and child classes you can target from your theme's CSS: .tablecrafter-container, .tc-table, .tc-table-container, .tc-global-search, .tc-filters, .tc-pagination, .tc-export-controls, .tc-loading, and .tc-error-state.

Asset loading in templates

You do not need to enqueue anything yourself. The shortcode calls register_assets() and enqueues the tablecrafter-frontend script (which depends on the tablecrafter-lib core library) and the tablecrafter-style stylesheet, then localizes the tablecrafterData JS object (with ajaxUrl, proxy nonce, and export nonces) for the proxy and export endpoints.

One caveat: WordPress enqueues happen during page rendering, so call do_shortcode() while the template is being output (inside the loop, header, footer, or a template part) — not from an early hook that fires before wp_enqueue_scripts. The bundled frontend.js auto-discovers every .tablecrafter-container on DOMContentLoaded and hydrates it, so multiple tables on one page each initialize independently.

Caching behavior to expect

The first PHP render of a given source fetches synchronously and stores both the HTML and parsed data in a transient (tc_html_…) keyed by source plus the display attributes, for one hour. Subsequent renders serve that cached HTML instantly; if the cached copy is older than five minutes, a background tc_refresh_single_source event is scheduled so the next visitor sees fresh data without a blocking fetch. Changing any of source, include, exclude, search, filters, export, per_page, or sort produces a different cache key, so variants never collide.

Equivalence with the block and Elementor widget

Whichever surface you choose resolves to the same shortcode. The Gutenberg block's render callback maps its attributes onto render_table(), and the Elementor widget assembles a [tablecrafter source="..."] string from its controls and runs it through do_shortcode(). That means the attribute names and behavior documented here are authoritative across all three integration methods — what you learn in PHP transfers directly to the editor surfaces.

Next, see shortcode-reference.html for the full attribute catalog with examples, and gutenberg-block.html for the no-code editor equivalent of these same options.