How TableCrafter Works

TableCrafter turns any remote or local data source into a responsive, crawlable WordPress table using a four-stage pipeline: fetch the source, cache it with stale-while-revalidate, render the table server-side for the first paint, then hydrate it in the browser for sorting, search, and export. No custom database tables are ever created.

SWR caching Server-side render Client hydration Zero-DB JSON / CSV / Sheets / API

The pipeline at a glance

Every table you place with the [tablecrafter] shortcode (or the tablecrafter/data-table block) flows through the same four stages. Understanding them explains why the first paint is fast, why search engines see real rows, and why your data stays at its source.

  1. Fetch source — TableCrafter reads the source URL. Local files are read straight from disk; remote JSON is fetched over cURL; Google Sheets and .csv URLs are parsed into rows.
  2. Cache (SWR) — The parsed data and rendered HTML are stored as WordPress transients. Stale entries are served instantly while a background refresh runs.
  3. Server-side render — On a cache miss, PHP builds a complete <table class="tc-table"> so the page ships with real, indexable rows in the initial HTML.
  4. Client hydrate — The frontend script attaches sorting, search, pagination, filtering, and export to the server-rendered DOM without re-fetching or re-drawing it.
â„šī¸

TableCrafter is a presentation layer, not a data store. It reads from your source and caches the result — it never copies your data into a custom database table.

Stage 1 — Fetching the source

The source attribute drives everything. TableCrafter inspects the URL and chooses the right reader inside fetch_data_from_source():

Source typeHow it is read
Local file (same site or plugin URL)Resolved to an on-disk path and read directly — no HTTP loopback. Path traversal is blocked by a realpath whitelist limited to ABSPATH, WP_CONTENT_DIR, and the plugin folder.
Google Sheet (docs.google.com/spreadsheets)Detected by pattern and parsed via the CSV source reader.
CSV URL (ends in .csv)Fetched and parsed into an array of row objects.
Remote JSON APIFetched over cURL with SSL verification on, a 30s timeout, and a 10s connect timeout, then decoded.

Remote URLs first pass an SSRF guard (is_safe_url()) that blocks local and private IP ranges. If the source returns a non-200 status or invalid JSON, the fetch returns a WP_Error that becomes a friendly error state downstream.

💡

When your JSON wraps the rows in a parent object (for example {"results":{"items":[...]}}), point the root attribute at the array path with dot notation: root="results.items".

Stage 2 — Caching with stale-while-revalidate

TableCrafter keeps two transient layers, both keyed by content so different configurations never collide:

Both transients live for one hour (HOUR_IN_SECONDS). The stale-while-revalidate behavior kicks in earlier: once a cached entry is older than the 5-minute stale threshold, the visitor is still served the cached HTML immediately, and a single background event (tc_refresh_single_source) is scheduled to rebuild it. Visitors never wait on a slow upstream source.

// Stale-while-revalidate, simplified from render_table()
if ( time() - $timestamp > ( 5 * MINUTE_IN_SECONDS ) ) {
    // Serve the stale HTML now, refresh in the background
    wp_schedule_single_event( time(), 'tc_refresh_single_source', array( $atts ) );
}

An hourly cron job (tc_refresher_cron) also warms every URL TableCrafter has seen, so popular tables are usually pre-built before anyone requests them. Caches clear automatically on a version bump, and you can purge them manually:

wp tablecrafter clear-cache   # remove every TableCrafter transient
wp tablecrafter warm-cache    # pre-fetch all tracked source URLs

Stage 3 — Server-side render

On a cache miss, fetch_and_render_php() builds the table in PHP. It applies your include/exclude column choices (including key:Alias header renaming), optional sort (column:direction), and emits semantic, accessible markup:

The finished HTML is wrapped in a container that carries every setting as a data attribute and is marked data-ssr="true", with the row data embedded as inline JSON for the hydration step:

<div id="tc-..." class="tablecrafter-container"
     data-source="https://api.example.com/data.json"
     data-search="true" data-ssr="true">
  <table class="tc-table"> ... </table>
  <script type="application/json" class="tc-initial-data">[ ... ]</script>
</div>

If a logged-in administrator hits a fetch error, an inline TableCrafter Setup Guide with troubleshooting tips is shown instead; regular visitors see a quiet .tc-error-state message.

Stage 4 — Client hydration

When the page loads, frontend.js finds every .tablecrafter-container on DOMContentLoaded and constructs a TableCrafter instance for each. Because the container is flagged data-ssr="true" and already contains a rendered .tc-table, the library hydrates rather than redraws:

Interactive features that need fresh data (live search across all rows, refresh) go through a nonce-protected AJAX proxy, tc_proxy_fetch, which reuses the same cache and SSRF protections as the server render. The library also emits DOM events you can listen for:

EventFires when
tablecrafter:cardTapA responsive card is tapped on mobile.
tablecrafter:cardViewA row's card view is opened.
tablecrafter:cardEditA row's edit action is triggered.

Why zero-DB matters

TableCrafter never registers a custom post type or a custom database table. The only thing it writes is transients — WordPress's standard, self-expiring cache — plus a small tc_tracked_urls option for cache warming. The practical consequences:

A complete example

This shortcode renders a paginated, searchable product table, shows only three columns (with a friendly header alias), pre-sorts by price descending, enables export, and refreshes every five minutes:

[tablecrafter source="https://api.example.com/products.json"
  root="data.items"
  include="name, price, stock:In Stock"
  sort="price:desc"
  per_page="25"
  search="true"
  export="true"
  auto_refresh="true"]

With export="true", the table gains a download menu. Exports are produced by the canonical export handler (includes/class-tc-export-handler.php), which generates a genuine OOXML .xlsx workbook via ZipArchive and a real PDF — not a CSV renamed with a different extension.

Where to set it up in wp-admin

Open WordPress Admin → TableCrafter. The dashboard offers one-click Quick Start demos (User Directory, Product Inventory, Sales Metrics, Employee CSV, Google Sheet, Airtable), a settings panel where you paste a Data Source URL, upload a CSV/JSON file, or connect a Sheet, and a Live Preview pane. When the preview looks right, copy the generated shortcode from the Usage card and paste it into any post, page, or block. In the block editor, search for the tablecrafter/data-table block to configure the same options visually.

âš ī¸

A change to your include, exclude, sort, or per_page values produces a new cache key, so the new layout appears on the next render. If you change the upstream data itself and want it live immediately, run wp tablecrafter clear-cache rather than waiting for the hourly refresh.

Next steps: see shortcode-reference.html for every attribute in detail, and caching-and-performance.html to tune the stale-while-revalidate behavior and cron warming.