Recipe: Product Catalog
Turn a JSON or CSV product feed into a responsive, searchable, paginated catalog on any WordPress page using a single [tablecrafter] shortcode. This recipe builds a real working catalog from the plugin's bundled products.json and explains every attribute it touches.
What you'll build
A catalog table of products (SKU, name, category, price, stock, rating) that visitors can search and page through. The data lives in a flat file, the table is server-rendered for SEO, and the browser hydrates it into an interactive grid. No database tables, no custom code.
TableCrafter ships a ready-to-use sample file you can point at immediately:
wp-content/plugins/tablecrafter-wp-data-tables/demo-data/products.json
It is a flat JSON array of objects, which is exactly the shape TableCrafter renders without any extra configuration:
[
{
"sku": "PRD-001",
"product_name": "Wireless Headphones",
"category": "Electronics",
"price": 99.99,
"stock_level": 45,
"rating": 4.5
}
]
The source must resolve to a list of objects (or a CSV with a header row). A nested object can be reached with the root attribute. A bare list of scalars is auto-wrapped into a single Value column.
Step 1 — Host your product data
Put your catalog data somewhere the site can read it. Three source styles are supported, and TableCrafter routes them automatically by file extension:
- Local JSON/CSV — upload to your Media Library or a plugin/theme folder. URLs under your own site root are read straight from disk (faster, and they bypass loopback HTTP).
- Remote JSON — any public URL returning a JSON array.
- Remote CSV / Google Sheets — a URL ending in
.csv, or a Google Sheets share link, is fetched and parsed into rows. The first CSV line is treated as the header.
For this recipe, use the bundled demo file. Its full URL is your site URL plus the plugin path shown above.
Step 2 — Drop in the shortcode
Edit any page or post and add the [tablecrafter] shortcode. The minimum viable catalog only needs a source:
[tablecrafter source="https://yoursite.com/wp-content/plugins/tablecrafter-wp-data-tables/demo-data/products.json"]
That alone gives you a sortable table. Click any header to sort; the headers are keyboard-focusable (Tab then Enter or Space) and carry aria-sort state for screen readers.
Prefer not to hand-type URLs? Go to wp-admin → TableCrafter. The dashboard includes a live preview playground and a shortcode generator that builds the tag for you from a source dropdown.
Step 3 — Add search and pagination
A catalog needs a search box and pages. Turn both on:
[tablecrafter
source="https://yoursite.com/.../demo-data/products.json"
search="true"
per_page=10]
Here is what each switch does:
search="true"renders a global search input (.tc-global-search) above the table. Typing filters every column at once. Input is debounced by 300 ms and resets you to page 1 on each keystroke.per_page="10"enables client-side pagination at 10 rows per page. Pagination controls (.tc-pagination) appear only when the filtered row count exceeds the page size, so a short result set stays clean.
The pagination bar adapts to dataset size: a Previous / Next pair always shows, a page-jump number input appears past 5 pages, and First / Last jumps past 10 pages. A "Rows per page" selector (.tc-page-size-select) offers 10, 25, 50, 100, and 250.
Even if you omit per_page, TableCrafter auto-enables pagination once a dataset crosses 1,000 rows (its large-dataset threshold) so big catalogs never render thousands of DOM rows at once.
Step 4 — Pick and rename columns
Raw field keys like product_name and stock_level are not customer-friendly. Use include to choose columns and rename them with the key:Alias syntax. Column order follows the order you list:
[tablecrafter
source="https://yoursite.com/.../demo-data/products.json"
search="true"
per_page=10
include="product_name:Product, category:Category, price:Price, rating:Rating"
sort="price:desc"]
This catalog now shows four friendly columns, hides the internal sku and stock_level fields, and lands pre-sorted by price, highest first. Notes on these attributes:
includeis a comma-separated allowlist. Append:Labelto any key to override the auto-generated header.excludeis the inverse — list keys to drop while keeping everything else. Use one or the other, not both.sorttakescolumn:directionwhere direction isascordesc. Numeric columns sort numerically; text sorts case-insensitively.
Step 5 — Let visitors export the catalog
Add export="true" to expose download controls (.tc-export-controls):
[tablecrafter source="..." search="true" per_page=10 export="true"]
This renders an export dropdown offering CSV, XLSX, and PDF, plus a Copy to Clipboard button that yields tab-separated text ready to paste into a spreadsheet. The XLSX and PDF files are produced server-side by the export handler at includes/class-tc-export-handler.php — they are genuine spreadsheet and PDF binaries, not renamed CSVs.
Full attribute reference
Every attribute below is real and used by the catalog render path. Booleans accept true, 1, or yes.
| Attribute | Default | Required? | Purpose |
|---|---|---|---|
| source | empty | Required | URL of the JSON or CSV file. Empty source renders a placeholder. |
| include | empty | Optional | Comma-separated columns to show; supports key:Alias renaming and sets order. |
| exclude | empty | Optional | Comma-separated columns to hide. |
| root | empty | Optional | Dot-path into a nested JSON object to find the list (e.g. data.products). |
| search | false | Optional | Show the global search box. |
| filters | true | Optional | Show per-column filters (auto-detected as text, multiselect, number range, or date range). |
| per_page | 0 | Optional | Rows per page. 0 = no pagination (auto-enables past 1,000 rows). |
| sort | empty | Optional | Initial sort as column:asc or column:desc. |
| export | false | Optional | Show CSV/XLSX/PDF export and Copy-to-Clipboard controls. |
| id | auto | Optional | Custom container ID; defaults to a generated tc-... value. |
| auto_refresh | false | Optional | Periodically re-poll the source; pair with refresh_interval (ms, default 300000). |
How rendering works under the hood
Understanding the pipeline helps when you debug a catalog that looks wrong:
- Server render (SSR). On first view PHP fetches the source, applies
root,include/exclude, andsort, then emits a real<table class="tc-table">with sortable<th class="tc-sortable">headers. This HTML is crawlable, so search engines see your products. - Stale-while-revalidate caching. The rendered HTML and parsed data are cached in transients. Stale entries (older than 5 minutes) trigger a background refresh via a scheduled event, so visitors never wait on a slow source.
- Hydration. The shipped
frontend.jsfinds every.tablecrafter-container, reads itsdata-*attributes (data-search,data-per-page,data-export, etc.), and the core library wires up search, pagination, sorting, and export on top of the server HTML.
If an admin (a user who can manage_options) loads a page where the source fails, TableCrafter shows a detailed inline error helper. Logged-out visitors see only a friendly "Unable to load data" message, so broken feeds never expose internals to the public.
Styling the catalog
The frontend stylesheet assets/css/tablecrafter.css exposes stable, prefixed classes you can target from your theme:
| Class | Element |
|---|---|
| .tablecrafter-container | Outer wrapper holding the data-* config. |
| .tc-wrapper | Inner shell that groups search, filters, table, and pagination. |
| .tc-table | The product table itself. |
| .tc-global-search-container | Wrapper around the search input. |
| .tc-pagination | Pagination bar (info, controls, page-size select). |
| .tc-export-controls | Export dropdown and clipboard button. |
| .tc-cards-container | Card layout used on mobile breakpoints instead of a wide table. |
For programmatic hooks, the container dispatches bubbling tablecrafter:* CustomEvents (such as tablecrafter:cardTap, tablecrafter:cardView, and tablecrafter:cardEdit) you can listen for to extend mobile card interactions.
Troubleshooting
- "Structure Error: the target data is not a list." Your JSON top level is an object, not an array. Point
rootat the array, e.g.root="results". - Wrong or missing columns. Field keys are case-sensitive and must match the source exactly. Verify the keys in your file before listing them in
include. - Pagination not appearing. Controls only show when filtered rows exceed
per_page. With six demo products, setper_page="3"to see paging in action. - Stale data after editing the file. The SWR cache holds for up to an hour; it refreshes in the background within ~5 minutes, or you can bump the page to force a fresh first render.
Next, see data-sources-json.html to dig into the root path and remote-fetch options, and recipe-csv-import.html to adapt this same catalog to a CSV or Google Sheets feed.