Custom Connector Reference
Last updated: August 2026
A Custom connector lets you write your own PHP logic to feed a Swiffle Grid block — a live API call, a database query, or a fully embedded dataset. It’s the same mechanism used internally by the built-in WordPress, WooCommerce, Google Sheets, CSV, and RSS sources, just exposed for your own code.
get_data() function already wired up. Rename the folder and function names, fill in your own logic, and you’re done.1. Package structure and registration
A Custom connector is a standalone WordPress plugin, installed the same way as any other plugin — via Plugins → Add New → Upload Plugin. It never runs inside Swiffle Grid’s own settings screen; it simply announces itself to Swiffle Grid through a filter hook, so Swiffle Grid knows it exists and how to call it.
Your plugin’s main file needs three things: a standard plugin header, an ABSPATH guard, and a registration block:
<?php
/*
Plugin Name: My Connector
Requires Plugins: swiffle-grid-embed-data-anywhere
*/
if ( ! defined( 'ABSPATH' ) ) exit;
add_action( 'plugins_loaded', function () {
add_filter( 'swiffle_grid_register_connector', function ( $connectors ) {
$connectors['swg_connector_myslug'] = [
'label' => __( 'My Connector', 'my-connector' ),
'get_data' => 'swiffle_custom_myslug_get_data',
'get_filter_form' => 'swiffle_custom_myslug_get_filter_form', // optional
];
return $connectors;
} );
} );
swg_connector_myslug above) must start with swg_connector_. Swiffle Grid silently ignores any registered slug that doesn’t — this guarantees a connector can never collide with (or accidentally shadow) a built-in source type such as wp, woo, csv, or rss.| Registration key | Required? | Purpose |
|---|---|---|
label | Yes | Name shown in the block’s «Source» dropdown and in Swiffle Grid’s admin screen |
get_data | Yes | Name of the function that returns the data for the grid |
get_filter_form | No | Name of the function that returns the filter form. Omit this key entirely if your connector has no filter. |
Once your plugin is active, its slug (myslug in the example above — without the swg_connector_ prefix) appears automatically in the «Source» dropdown of every Swiffle Grid block set to Custom, and the connector is listed under Swiffle Grid in the WordPress admin sidebar for reference. There is nothing to import, edit, or delete from within Swiffle Grid itself — manage the connector the same way you’d manage any other plugin, under Plugins → Installed Plugins.
register_setting() + a form under options.php, storing the value via wp_options) and read it back with get_option() inside get_data(). This keeps secrets out of any file that might get copied, backed up, or shared.2. The data function
function swiffle_custom_{slug}_get_data($pagesize, $strl, $parameters) {
// ... your logic here ...
return [$posts, $have_more, $css, $script];
}
Parameters
| Parameter | Type | Meaning |
|---|---|---|
$pagesize | int | Maximum number of cards to return for this page load. Set by the «Cards per page» block setting. |
$strl | int (passed as string) | Zero-based offset for pagination. 0 on the first page, $pagesize on the second, and so on. |
$parameters | array | All contextual data available to the connector: filter values, block settings, and more. See below. |
Filter values
Any field in your filter form with name="FILTR_xxx" arrives as $parameters['xxx'] — the FILTR_ prefix is stripped automatically. For example, name="FILTR_year" maps to $parameters['year'].
Block parameters — $parameters['swiffle_grid']
All block settings configured in Gutenberg or Elementor are available as a nested array under $parameters['swiffle_grid']. Most parameters exist in a desktop variant and a phone variant (key ending in p); your connector can use whichever is relevant to its logic.
p). The badge Laptop and Phone below indicate which variant a key represents. When a parameter exists on both devices, only one row is shown with Both.Show all block parameters (70+ entries)
Key in $parameters['swiffle_grid'] | Device | Description |
|---|---|---|
proxy | Both | Site home URL — the WordPress installation that serves the connector |
postssource | Both | Active data source: category, rss, xlsx, csv, csvs, woocategory, custom |
postsurl | Both | URL or slug of the source (RSS feed URL, CSV URL, custom connector slug, etc.) |
postscategory | Both | Comma-separated list of category slugs (WordPress or WooCommerce) |
orderby | Both | WooCommerce sort order: date,DESC / popularity,DESC / rating,DESC / price,DESC / price,ASC / menu_order,DESC |
filtertitle | Both | Custom title shown in the filter panel (empty = auto) |
followpagefilter | Both | true / false — whether the block follows a page-level filter |
followurlfilter | Both | true / false — whether the block follows URL-based filters (WooCommerce only) |
shownodataerr | Both | true / false — whether to display an error message when no data is returned |
includeheaders | Both | true / false — include WordPress post headers (WordPress source only) |
blocklang | Both | Language preference: wp (follow WordPress locale via get_locale()), or an explicit locale code such as fr_FR, en_US, de_DE, es_ES, it_IT, pt_PT, pt_BR, nl_NL, pl_PL, ru_RU, ja_JP, ko_KR, zh_CN, hi_IN, ar_SA |
blockcardsperpg | Laptop | Cards per page (desktop). Same value as $pagesize on desktop. |
blockcardsperpgp | Phone | Cards per page (phone) |
blockdeflayout | Laptop | Default layout: wrap / swipe / swipefull / row |
blockdeflayoutp | Phone | Default layout (phone) |
blockshowlayout | Laptop | Layout toggle button shown to the visitor: no / all / wrap / swipe / swipefull / row |
blockshowlayoutp | Phone | Layout toggle button (phone) |
filterbtnshow | Both | true / false — show the filter button |
minicartbtnshow | Laptop | true / false — show the mini cart (WooCommerce only) |
minicartbtnshowp | Phone | Show mini cart (phone, WooCommerce only) |
minicartbtnshape | Laptop | Mini cart shape: SF (square with all) / S (square, quantity only) / RF (round with all) / R (round, quantity only) |
minicartbtnshapep | Phone | Mini cart shape (phone) |
minicartico | Both | Mini cart icon: bag / cart / basket |
minicartlogin | Both | true / false — show login button inside mini cart (WooCommerce only) |
minicartbtnlocation | Laptop | Mini cart position: L / T / R / B / H (HTML element, uses CSS selector below) |
minicartbtnlocationp | Phone | Mini cart position (phone) |
minicartbtnselector | Laptop | CSS selector for the HTML element that hosts the mini cart (when location is H) |
minicartbtnselectorp | Phone | CSS selector (phone) |
minicartbtnmargin | Laptop | Mini cart margin from the edge (e.g. 20vh) |
minicartbtnmarginp | Phone | Mini cart margin (phone) |
carddirection | Laptop | Card front direction: row / column |
carddirectionp | Phone | Card front direction (phone) |
cardimgadjustin | Laptop | Image adjustment: cover / height100 / width100 |
cardimgadjustinp | Phone | Image adjustment (phone) |
cardimgsize | Laptop | Image quality (WP/WooCommerce): large / medium / thumbnail |
cardimgsizep | Phone | Image quality (phone) |
cardflowtitleshow | Both | true / false — show the card flow title |
cardflowtitle | Both | Card flow title (empty = auto-generated) |
cardtitleoverpicture | Laptop | true / false — render title overlaid on the image |
cardtitleoverpicturep | Phone | Title over picture (phone) |
cardtitlefontsize | Laptop | Title font size (CSS value, e.g. 16px, 2vw) |
cardtitlefontsizep | Phone | Title font size (phone) |
cardpriceshow | Laptop | true / false — show price (WooCommerce only) |
cardpriceshowp | Phone | Show price (phone) |
cardpriceoverpicture | Laptop | true / false — price overlaid on the image (WooCommerce only) |
cardpriceoverpicturep | Phone | Price over picture (phone) |
cardalroverpicture | Laptop | true / false — alert label overlaid on image (WooCommerce only) |
cardalroverpicturep | Phone | Alert over picture (phone) |
cardabstractshow | Laptop | true / false — show the card abstract/excerpt |
cardabstractshowp | Phone | Show abstract (phone) |
cardtocartshow | Laptop | true / false — show Add to Cart button (WooCommerce only) |
cardtocartshowp | Phone | Show Add to Cart (phone) |
cardrateshow | Laptop | true / false — show star rating (WooCommerce only) |
cardrateshowp | Phone | Show rating (phone) |
cardoptionsshow | Laptop | true / false — show product variations (WooCommerce only) |
cardoptionsshowp | Phone | Show variations (phone) |
cardiconshow | Laptop | true / false — show swipe icon on the card |
cardiconshowp | Phone | Show swipe icon (phone) |
cardpromoshow | Both | SALE badge animation (WooCommerce only): no / spin / jumper / marquee / woo / woojumper |
cardcontentonly | Both | true / false — strip the card chrome and render content only |
cardCartBtnRnd | Both | true / false — round the Add to Cart / quantity buttons (WooCommerce only) |
cardwidth | Laptop | Card width (CSS value, e.g. 300px, 30vw) |
cardwidthp | Phone | Card width (phone) |
cardwidthmax | Laptop | Maximum card width (CSS value) |
cardwidthmaxp | Phone | Maximum card width (phone) |
cardheight | Laptop | Card height (CSS value) |
cardheightp | Phone | Card height (phone) |
cardgap | Laptop | Gap between cards (CSS value) |
cardgapp | Phone | Card gap (phone) |
cardradiuspxl | Laptop | Card corner radius in px (integer) |
cardradiuspxlp | Phone | Card radius (phone) |
cardimgpct | Laptop | Image area as a percentage of the card height (0–100) |
cardimgpctp | Phone | Image percentage (phone) |
cardimgradiuspxl | Laptop | Card image corner radius in px (integer) |
cardimgradiuspxlp | Phone | Image radius (phone) |
cardbtcartsize | Laptop | Add to Cart button size 0–10 (WooCommerce only) |
cardbtcartsizep | Phone | Cart button size (phone) |
cardclick | Laptop | Card front click action: flip / modal/80 / modal/50 / modal/100 / navigate / navigatenew / navigateurl / navigateurlnew / none |
cardclickp | Phone | Card front click action (phone) |
cardnavurl | Both | Target URL (used when action is navigateurl or navigateurlnew for RSS, CSV, XLSX sources) |
cardnavshow | Both | true / false — show the navigate button (RSS, CSV, XLSX sources) |
blockshowcomments | Both | true / false — show comments (WordPress/WooCommerce sources) |
3. Return value
The function must return a 4-element indexed array:
return [$posts, $have_more, $css, $script];
$posts — two supported formats
A. Tabular / auto-rendered mode
The first element of $posts is a header row containing column names; every subsequent element is a data row with values in the same order. Swiffle Grid reads the column names and auto-generates the matching card element based on recognised prefixes:
| Prefix | Generates | Example column name |
|---|---|---|
TIT_ | Card title | TIT_CityName |
IMG_ | Card image — the first IMG_ column becomes the main front-card image | IMG_Photo1 |
IMGZ_ | Zoomable image | IMGZ_Detail1 |
IMGZFS_ | Full-screen zoomable image in modal | IMGZFS_Detail1 |
MAP_ | OpenStreetMap card generated from coordinates or an address | MAP_Headquarters |
NAV_ | URL opened when the card-click action is set to Navigate URL | NAV_OfficialWebsite |
SLIDE_ | Image or video embed added to the slide gallery | SLIDE_Photo2 |
SLIDEZOOM_ | Zoomable image or video embed in the zoom gallery | SLIDEZOOM_Photo2 |
SLIDEFULL_ | Zoomable + full-screen image (no video) in the gallery | SLIDEFULL_Photo2 |
OTHER | Additional HTML or plain-text content | OTHER_Description |
NAV_ column is defined, the card behaves as a direct link — visitors will not see the card’s back-side content (slide galleries, media, etc.). Use either a NAV_ column or back-side content, not both.Example — tabular mode:
$posts = [
['TIT_Title', 'IMG_Photo', 'OTHER_Description'], // header row
['Grand Canyon', 'https://example.com/grand_canyon.jpg', 'A vast canyon in Arizona.'],
['Mount Fuji', 'https://example.com/mount_fuji.jpg', 'Japan's iconic volcano.'],
];
B. Full custom HTML mode
When you need full control over the card layout, each element of $posts is an associative array with three keys:
$posts[] = [
'front' => '<div class="my-card">...</div>', // HTML for the card's front face
'back' => '<div class="my-back">...</div>', // HTML for the card's back face (shown on flip)
'navurl' => 'https://example.com/page', // destination URL; empty string if unused
];
$css (see below) must cover all classes used in your front and back HTML, since page-level theme CSS cannot reach inside the block’s Shadow DOM.$have_more
String 'y' or 'n'. Tells the block whether a next page of results exists. Set it to 'y' when more records are available beyond the current page, 'n' when the current page is the last one.
$css
A string of CSS, injected directly into the block’s own rendering scope. Because Swiffle Grid renders as a <swiffle-grid> custom element using a Shadow DOM, page-level theme CSS does not reach inside it. Every class and style your connector’s HTML relies on must be returned here.
$css = <<<CSS
.my-card { background:#0f172a; color:#e5e7eb; border-radius:16px; }
.my-card h3 { margin:0; font-size:18px; }
CSS;
$script
A JavaScript string executed after the block has finished rendering its HTML for the current page. Use it to wire up client-side behaviour that depends on the rendered card DOM (event listeners, third-party widget initialisation, etc.). It runs once per page load (i.e. once per pagination step).
$script = <<<SCRIPT
document.querySelectorAll('.my-card').forEach(card => {
card.addEventListener('click', () => console.log('Card clicked'));
});
SCRIPT;
4. The filter form function
function swiffle_custom_{slug}_get_filter_form() {
// ...
return $html . "n<!--SWIFFLE_SCRIPT-->n" . $js;
}
Returns a single string: the filter form’s HTML, followed by the literal separator <!--SWIFFLE_SCRIPT-->, followed by JavaScript that runs once the form is in the page. The function does not return a CSS value — all CSS for the filter form must be included inline in the HTML string itself (via a <style> tag or inline style attributes).
- Every input that should act as a filter must use
name="FILTR_xxx". TheFILTR_prefix is stripped and the remainder becomes the key in$parameters. - The JavaScript portion runs in the page context, not inside the Shadow DOM. To reach inside the block, use the standard pattern:
const getSwiffleBlock = () => {
for (const sg of document.querySelectorAll('swiffle-grid')) {
if (sg.shadowRoot?.querySelector('.fltfrm')) return sg;
}
return null;
};
const block = getSwiffleBlock();
Minimal example:
function swiffle_custom_myplugin_get_filter_form() {
$html = <<<HTML
<div class="form_control">
<div class="form_control_container">
<label for="my_year">Year</label>
<input type="number" id="my_year" name="FILTR_year" min="2000" max="2030" placeholder="E.g.: 2024">
</div>
</div>
HTML;
$js = <<<JS
(() => {
const block = (() => {
for (const sg of document.querySelectorAll('swiffle-grid')) {
if (sg.shadowRoot?.querySelector('.fltfrm')) return sg;
}
})();
if (!block) return;
// Add behaviour here if needed.
})();
JS;
return $html . "n<!--SWIFFLE_SCRIPT-->n" . $js;
}
5. Pagination patterns
The recommended approach is to request $pagesize + 1 records from your data source, then check whether that extra record exists to set $have_more, and remove it before returning the slice. This avoids a separate count query and works with any data source.
$limit = intval($pagesize) + 1; // fetch one extra
$offset = max(0, intval($strl));
// ... fetch $limit records from offset $offset ...
if (count($data) === $limit) {
$have_more = 'y';
array_pop($data); // remove the extra record
} else {
$have_more = 'n';
}
$posts = [['TIT_Title', 'IMG_Photo']]; // header row
foreach ($data as $row) {
$posts[] = [$row['title'], $row['image']];
}
You are free to use a different method — the only constraint is that $have_more must accurately reflect whether more pages exist. A separate count query, a date-window calculation, or any other logic that correctly sets this value is equally valid.
6. Practical recommendations
- Use
wp_remote_get(), notfile_get_contents()— some hosts disable theallow_url_fopenPHP setting.wp_remote_get()uses cURL when available, returns a real HTTP status code, and gives you aWP_Errorobject to check on failure instead of a silentfalse. - Set an explicit
user-agentheader — some external APIs return403 Forbiddento requests with an empty or generic user agent. A descriptive user agent also helps API owners identify legitimate traffic. - Cache responses for unreliable APIs — use
set_transient($key, $data, HOUR_IN_SECONDS)for a short-lived cache, andupdate_option($key, $data, false)for a durable fallback. On a transient miss, try the API; if the API fails, fall back to the last stored option. This ensures your grid stays visible even during brief outages. - Never hardcode credentials — store API keys and secrets via
register_setting()andget_option()on your own settings page (see section 1), never as a literal string or adefine()in your plugin file. - Escape all output — use
htmlspecialchars()for plain text,esc_attr()for HTML attribute values,esc_html()for inline text content, andesc_url()for URLs. - Handle empty results gracefully — always return a valid 4-element array, even when there is no data:
return [[], 'n', '', ''];
<<<CSS ... CSS;) in submitted code — replace it with plain string concatenation before submitting. This only matters if you intend to publish your connector publicly; for a private, personally-installed plugin, heredocs are perfectly fine and the examples above can be used as-is.