Grids & Layouts

A grid is the core display unit in Grid Panda. It binds a post query (source_config), a rendering layout and card template (grid_config), and a set of facets into a single embeddable component.

Database Structure

Grids are stored in the wp_gridpanda_grids table:

ColumnTypeDescription
idbigint unsignedAuto-increment primary key
namevarchar(255)Human-readable grid name
slugvarchar(200) UNIQUEURL-safe identifier used in [gridpanda_grid slug='...']
layoutvarchar(50)Layout type: grid, masonry, list, carousel, metro, justified
source_typevarchar(50)Type of data source: post_type, taxonomy
source_configlongtext (JSON)Query configuration — post type, status, orderby, etc.
card_templatelongtextCard template ID or inline HTML (legacy)
grid_configlongtext (JSON)Layout, pagination, and display configuration
created_at / updated_atdatetimeRecord timestamps

source_config — Query Configuration

The source_config JSON defines what posts are queried. Grid Panda passes it to the registered source handler, which converts it into WP_Query arguments:

Post Type Source (most common)

{
  "post_type":   "product",     // Any registered CPT or 'post', 'page'
  "post_status": "publish",     // publish, draft, pending, private, any
  "orderby":     "date",        // date, title, modified, ID, comment_count,
                                // menu_order, rand, meta_value, meta_value_num
  "order":       "DESC",        // ASC or DESC
  "posts_per_page": 12          // Default items per page (overridden by grid_config)
}

Taxonomy Archive Source

{
  "post_type":   "post",
  "taxonomy":    "category",    // Taxonomy to scope results to
  "term_id":     5,             // Optional: specific term, null = all terms
  "post_status": "publish",
  "orderby":     "date",
  "order":       "DESC"
}
Source handlers: Grid Panda has three built-in source handlers (post_type, taxonomy, user) registered via the gridpanda/grid/register_sources action. Custom source handlers can be added by registering a class that implements the source contract.

grid_config — Layout & Display Configuration

The grid_config JSON controls how results are rendered. Full structure:

{
  "card_id": 12,                  // Card template ID

  "layout": "grid",               // Layout engine to use

  "layout_settings": {
    "columns":         3,         // Desktop columns
    "tablet_columns":  2,         // Tablet columns
    "mobile_columns":  1,         // Mobile columns
    "gap":             "20px",    // Gap between items (CSS length)
    "row_gap":         "20px"     // Row gap (defaults to gap)
  },

  "per_page": 12,                 // Items per page

  "pagination_config": {
    "type":               "numbered", // numbered | load_more | infinite | none
    "position":           "bottom",   // bottom | top | both
    "alignment":          "center",   // left | center | right
    "page_range":         2,          // Pages on each side of current
    "show_ends":          true,       // Show first/last page links
    "show_results_info":  false,      // Show "Showing 1–12 of 48"
    "scroll_to_top":      true,       // Scroll to grid on page change
    "ajax":               true,       // AJAX pagination (no full reload)
    "infinite_threshold": 300,        // px from bottom to trigger load_more/infinite

    "labels": {
      "prev":         "Previous",
      "next":         "Next",
      "load_more":    "Load More",
      "loading":      "Loading…",
      "no_more":      "No more items",
      "results_info": "Showing {from}–{to} of {total}"
    }
  }
}

Pagination Types

TypeBehaviourNotes
numberedClassic numbered page navigation with prev/next arrowsWorks with SEO clean URLs; each page number gets its own URL
load_more"Load More" button appends the next page to the existing resultsResults accumulate — no items are removed. Button hides when no more pages.
infiniteAutomatically loads more results as the user scrolls to the infinite_threshold from the grid bottomThreshold defaults to 300px above grid bottom
noneRenders all matching posts in one pass with no paginationUse only for small result sets — loads all posts at once

Layout Types

The layout column determines the layout engine used to position cards. Each layout has its own configuration options within layout_settings:

gridCSS Grid

Fixed-column CSS grid. The most common and performant layout. Columns are set per breakpoint (mobile, tablet, desktop) with configurable gap.

layout_settings keys: columns (int), tablet_columns (int), mobile_columns (int), gap (CSS length), row_gap (CSS length)

masonryMasonry

Pinterest-style layout where items with variable height are stacked in columns without gaps between them. Uses column-based masonry (CSS columns or JS-calculated positions).

layout_settings keys: columns (int), tablet_columns, mobile_columns, gap (CSS length)

listList

Single-column full-width layout. Each card occupies the full container width. Good for article or blog post listings.

layout_settings keys: gap (CSS length between items)

carouselCarousel

Horizontal scroll carousel with previous/next navigation buttons. Shows a configurable number of cards at a time.

layout_settings keys: columns (visible cards), gap, autoplay (bool), autoplay_speed (ms), loop (bool), show_dots (bool), show_arrows (bool)

metroMetro

Mixed tile sizes using a defined repeating pattern. Some cards are full-width, half-width, or featured size. Pattern cycles through the result set.

layout_settings keys: pattern (array of size definitions), gap

justifiedJustified

Equal-height rows where each card's width is proportionally adjusted so the row fills the container exactly. Works best with images that have consistent aspect ratios.

layout_settings keys: target_row_height (int px), gap

Query Pipeline

When a grid render is requested (shortcode or REST API), Grid Panda executes the following pipeline:

  1. 1
    Resolve source: GridManager reads source_type and source_config, delegates to the matching SourceHandler to produce base WP_Query args.
  2. 2
    Apply grid config: Per-page, orderby, and order from grid_config (or request overrides) are merged into the query args.
  3. 3
    Inject facet filters: FilterParamHandler reads fx_ query params and calls each active facet type's get_query_args() to build tax_query/meta_query clauses. These are merged into the base args.
  4. 4
    Apply gridpanda/grid/query_args filter: Third-party code can modify the final WP_Query args via this filter before execution.
  5. 5
    Execute WP_Query: The main query returns the matching post IDs and total count.
  6. 6
    Calculate facet counts: For each facet, a complementary query runs against the active result set (excluding that facet's own filter) to compute accurate sibling counts from the index table.
  7. 7
    Render cards: Each post is passed through the card template engine (CardBuilder) which resolves dynamic tags and outputs HTML.
  8. 8
    Build response: HTML, pagination, facet counts, and metadata are assembled and returned as JSON.

Grid REST API

GET
/wp-json/gridpanda/v1/grids

List all grids

GET
/wp-json/gridpanda/v1/grids/{id}

Get a single grid by ID

POST
/wp-json/gridpanda/v1/grids/{id}/render

Render a grid with active facet selections

POST
/wp-json/gridpanda/v1/grids/preview

Preview a grid configuration (admin)

POST
/wp-json/gridpanda/v1/grids

Create a grid (admin)

PUT
/wp-json/gridpanda/v1/grids/{id}

Update a grid (admin)

DELETE
/wp-json/gridpanda/v1/grids/{id}

Delete a grid (admin)

Render Request & Response

// Request (POST)
POST /wp-json/gridpanda/v1/grids/5/render
{
  "facets": {
    "color": ["red", "blue"],
    "size": ["medium"]
  },
  "page": 1,
  "per_page": 12,
  "orderby": "date",
  "order": "DESC"
}

// Response
{
  "html":        "<div class='gridpanda-grid'>…</div>",
  "total":       48,
  "total_pages": 4,
  "page":        1,
  "per_page":    12,
  "facet_counts": {
    "color": { "red": 12, "blue": 9, "green": 5 },
    "size":  { "small": 8, "medium": 20, "large": 15 }
  },
  "pagination_html": "<nav class='gridpanda-pagination'>…</nav>"
}

Grid Hooks

gridpanda/grid/query_argsfilter
$query_args, $grid

Modify the WP_Query args before grid execution. Use to add custom constraints, change order, or inject additional meta/tax queries.

gridpanda/grid/before_renderaction
$grid_id_or_slug, $args

Fires before the grid renders. Useful for conditional cache busting or analytics.

gridpanda/grid/after_renderaction
$grid_id_or_slug, $html, $query

Fires after render with the final HTML and WP_Query object.

gridpanda/grid/htmlfilter
$html, $grid, $posts

Modify the final rendered grid HTML before it is returned.

gridpanda/grid/item_datafilter
$item_data, $post, $grid

Customize the data passed to the card template for each post.

gridpanda/grid/dynamic_tagsfilter
'', $tag, $post, $is_raw

Handle custom dynamic tags not built into Grid Panda. Return non-empty string to handle the tag.

gridpanda/grid/register_sourcesaction
$source_manager

Register custom data source handlers.