This is the technical reference for Kinetra 1.3.0: how a request becomes the animated site, every PHP hook, the data the frontend receives, the browser events, and how to build on it safely. It assumes you know WordPress themes and plugins. Site owners don’t need any of it; start with After installation instead.
How Kinetra works
Kinetra is a normal PHP theme that ships a prebuilt Next.js and React app. There is no Node.js on the server. Every request goes through WordPress as usual:
- WordPress runs its query and loads
index.php, the theme’s only page template. kinetra_build_payload()turns the current query into a JSON payload (the page), andkinetra_site_data()builds the site data (menus, settings, labels).- The theme prints plain, crawlable HTML of the page in
#kinetra-ssrfor search engines and no-JavaScript visitors, followed by both JSON objects in<script id="__KINETRA__" type="application/json">. - The app starts, reads that JSON, renders the animated page in
#kinetra-rootand adds the classnx-readyto<html>. - When a visitor clicks an internal link, the app fetches the same URL with
?_kinetra=1, which returns just the payload as JSON, then animates to the new page and updates the title, meta tags and history.
Because the page is always built by WordPress, every plugin that changes the query, content or SEO data keeps working. Plugins that need a real page load can ask for one (see
kinetra_needs_full_load).
Files
| Path | What it does |
|---|---|
kinetra/functions.php | Defines KINETRA_VERSION, KINETRA_DIR, KINETRA_URI and loads inc/. |
inc/options.php | Option schema, presets, palettes, fonts; kinetra_opt(). |
inc/payload.php | The payload, site data, menus and the ?_kinetra JSON endpoint. |
inc/render.php | The crawlable HTML and the app root. |
inc/assets.php | App files, CSS variables, fonts, publishing to uploads. |
inc/compat.php | Cache clearing, optimizer exclusions, full-load detection, breadcrumbs. |
inc/elementor.php, inc/elementor-hf.php | Elementor pages, Theme Builder, style kits, header and footer templates. |
inc/woocommerce.php | Product data, shop archives, cart drawer, native WooCommerce pages. |
inc/i18n.php | Frontend labels, language switchers, WPML/Polylang strings. |
inc/importer.php, inc/demo/ | Demo registry, packages and import. |
inc/license.php, inc/plugins.php, inc/admin.php, inc/wizard.php | Licence, plugin installer, Kinetra admin screens, Setup Wizard. |
kinetra/app/ | The built frontend. Don’t edit; rebuild from source. |
kinetra-core/inc/ | Projects, page meta, section blocks, SEO, contact, newsletter, custom code, uploads, Elementor widgets. |
Constants
| Constant | Where to set it | Effect |
|---|---|---|
KINETRA_DEV_ORIGIN | wp-config.php | Allows a frontend dev server (for example http://localhost:3000) to read the JSON endpoint. |
KINETRA_DEMO_SERVER | wp-config.php | URL of a folder of demo packages ({id}.json) to import from. |
KINETRA_VERSION, KINETRA_DIR, KINETRA_URI | Read only | Theme version, path and URL. |
KINETRA_CORE_VERSION, KINETRA_CORE_DIR, KINETRA_CORE_URI | Read only | Kinetra Core version, path and URL. Check defined( 'KINETRA_CORE_VERSION' ) before using Core functions. |
Where to put your code
- A small plugin or a child theme’s
functions.phpfor PHP. Both load before the parent theme, so filters are in place in time. - Kinetra → Custom Code for tracking tags and CSS. Snippets go in
wp_head,wp_body_openorwp_footerwith a priority and a condition (every page, front page only, logged-in users and so on). - Customize → Additional CSS for style tweaks.
A child theme only needs a style.css:
/*
Theme Name: Kinetra Child
Template: kinetra
*/
Don’t add
page.php,single.php,archive.phpor similar to a child theme. WordPress would use them instead of Kinetra’sindex.php, and those pages would lose the animated site.comments.phpcan be overridden; its output is shown inside the app.
Settings
Every Customizer option is defined once in kinetra_option_schema(). The schema registers the Customizer control, sanitises the value, gives it a default, and (unless public is false) sends it to the frontend as site.settings.
| Function | Returns |
|---|---|
kinetra_opt( $key ) | A setting’s value, typed and with the active preset and palette defaults applied. Text is translated with WPML or Polylang. |
kinetra_default( $key ) | The default: the colour palette first, then the style preset, then the schema. |
kinetra_style() | The active preset key, such as kinetic or launch. |
kinetra_public_settings() | All public settings, plus copyright with {year} and {site} filled in. |
kinetra_core_opt( $key ) | The same as kinetra_opt(), with fallbacks when the theme isn’t active (Kinetra Core). |
Values are stored as theme mods named kinetra_{key}, so get_theme_mod( 'kinetra_accent' ) gives the saved value only. Use kinetra_opt() to get the value with defaults.
A schema entry has section, type, label and default, and optionally description, choices (select), min/max (range, number), public and core. Types are select, toggle, color, range, number, text, textarea, url, email, code and palette.
Sections: kinetra_style, kinetra_colors, kinetra_typography, kinetra_layout, kinetra_header, kinetra_motion, kinetra_portfolio, kinetra_blog, kinetra_shop, kinetra_footer, kinetra_social, kinetra_contact, kinetra_seo, kinetra_advanced.
Add your own option
add_filter( 'kinetra_option_schema', function ( $schema ) {
$schema['promo_text'] = array(
'section' => 'kinetra_header',
'type' => 'text',
'label' => 'Promo text',
'default' => '',
);
return $schema;
} );
// Anywhere later:
$text = kinetra_opt( 'promo_text' );
The schema is built once per request and then cached. Add this filter when your file loads, not inside a later hook such as
init.
Design tokens (CSS variables)
The settings are printed as CSS custom properties on :root. Use them in your own CSS so it follows the Customizer and the light/dark switch:
| Variable | From |
|---|---|
--nx-accent, --nx-accent-text | Accent and the text colour on it |
--nx-bg, --nx-surface, --nx-text, --nx-muted, --nx-border, --nx-highlight | The light or dark palette, whichever is showing |
--nx-font-heading, --nx-font-body | Font stacks |
--nx-base, --nx-heading-weight, --nx-tracking, --nx-scale | Text size, heading weight, letter spacing, display scale |
--nx-container, --nx-gutter, --nx-section | Content width, side padding, space between sections |
--nx-radius, --nx-btn-radius | Corner radius of cards and buttons |
--nx-marquee | Marquee loop duration |
The active colour scheme is on <html data-scheme="light|dark">. <body> has the classes kinetra and kinetra-style-{preset}.
.my-badge {
background: var(--nx-accent);
color: var(--nx-accent-text);
border-radius: var(--nx-btn-radius);
font-family: var(--nx-font-heading);
}
:root[data-scheme="dark"] .my-badge { box-shadow: 0 0 0 1px var(--nx-border); }
Presets, palettes and fonts
// Change a preset's defaults.
add_filter( 'kinetra_presets', function ( $presets ) {
$presets['launch']['defaults']['accent'] = '#0EA5E9';
return $presets;
} );
// Add a colour combination to Customize → Colors.
add_filter( 'kinetra_palettes', function ( $palettes ) {
$palettes['brand'] = array(
'label' => 'Our brand',
'colors' => array( 'accent' => '#0B5FFF', 'accent_text' => '#FFFFFF', 'light_highlight' => '#0B5FFF', 'dark_highlight' => '#8DB2FF' ),
);
return $palettes;
} );
// Add a Google Font to the font choices.
add_filter( 'kinetra_fonts', function ( $fonts ) {
$fonts['Lexend'] = array( 'spec' => 'wght@400;500;600;700', 'fallback' => 'sans-serif' );
return $fonts;
} );
A new preset key works too. It sets option defaults and uses the Kinetic section layouts; the six built-in layouts are part of the frontend app.
Content and sections
A page is turned into a list of sections. Each kinetra/* block becomes { type: "block", name, data }. Everything between them (paragraphs, images, embeds, shortcodes, other plugins’ blocks) is rendered by WordPress and grouped into { type: "html", html }. Classic-editor content becomes one HTML section.
Each section is defined once in kinetra_block_schemas() in Kinetra Core. The schema registers the block (kinetra/{name}), builds its editor form, registers the Elementor widget (kinetra-{name}), renders the crawlable HTML, and shapes the data the app gets.
'hero' => array(
'title' => 'Hero',
'description' => 'Big animated headline with intro text and buttons.',
'icon' => 'superhero', // Dashicon name
'fields' => array(
'eyebrow' => array( 'type' => 'text', 'label' => 'Small label', 'default' => '' ),
'image' => array( 'type' => 'image', 'label' => 'Side image', 'default' => 0 ),
// …
),
),
Field types: text, textarea, url, color, number (min, max), toggle, select (choices), image (attachment ID; the app gets url, width, height, alt, srcset) and list (a repeater with its own fields).
Section names: hero, marquee, projects, feature, services, tabs, showreel, about, process, quote, clients, logos, testimonials, team, stats, features, pricing, faq, cta, newsletter, contact, products, categories, promo, banners, perks.
Change section defaults or fields
add_filter( 'kinetra_block_schemas', function ( $schemas ) {
// New Hero blocks start with your own button text.
$schemas['hero']['fields']['primary_label']['default'] = 'Book a call';
// Give the Call to Action a field your own code can read.
$schemas['cta']['fields']['campaign'] = array( 'type' => 'text', 'label' => 'Campaign ID', 'default' => '' );
return $schemas;
} );
The built app only draws the 26 sections it knows, and only the fields each one was written for. A new field reaches the payload (and the
kinetra_payloadfilter) straight away; to show it, or to add a completely new section, change the frontend source and rebuild. A new section name without a frontend component renders nothing on the animated site.
Replace a page’s sections
kinetra_pre_content_sections runs before the content is parsed. Return an array of sections to skip parsing, or null to continue.
add_filter( 'kinetra_pre_content_sections', function ( $sections, $post ) {
if ( 'landing' !== $post->post_name ) {
return $sections; // null: parse as usual
}
return array(
array( 'type' => 'block', 'name' => 'hero', 'data' => kinetra_block_data( 'hero', array( 'title' => "Hello\nworld" ) ) ),
array( 'type' => 'html', 'html' => '<p>Rendered by my plugin.</p>' ),
);
}, 10, 2 );
Post types, fields and meta
| Item | Details |
|---|---|
| Projects | Post type kinetra_project, taxonomy kinetra_project_cat. The URL slug is the portfolio_slug option (default work); categories use {slug}-category. Supports title, editor, thumbnail, excerpt, page attributes, revisions and custom fields. In the REST API. |
| Project meta | _kinetra_client, _kinetra_role, _kinetra_year, _kinetra_live_url, _kinetra_art_bg, _kinetra_art_fg, _kinetra_featured |
| Page and post meta | _kinetra_hide_title, _kinetra_seo_title, _kinetra_seo_description, _kinetra_noindex. Registered with show_in_rest. |
| Demo markers | _kinetra_demo on everything an import created. Remove demo content deletes items with this meta. |
| Elementor templates | _kinetra_hf (header or footer) and _kinetra_hf_hash on the templates Kinetra creates. |
| Menus | primary (Main menu, with drop-downs) and footer. |
| Widget areas | footer-widgets and the shop sidebar. |
| Image sizes | kinetra-card (1040 × 880, cropped) and kinetra-wide (1920 × 1080). |
| Shortcode | [kinetra_year] prints the current year. |
| Options | kinetra_subscribers (newsletter list), kinetra_code_snippets (Custom Code). |
Useful functions: kinetra_get_projects( array( 'count' => 4, 'category' => 'branding', 'featured' => true ) ), kinetra_project_card( $post ), kinetra_post_card( $post ), kinetra_image( $id, $size ), kinetra_menu( $location ).
The payload
The payload is what the app draws for one URL. Change it with kinetra_payload. It always has kind, seo (title, description, canonical, image, noindex), url and lang.
| kind | Main keys |
|---|---|
single | postType, id, title, excerpt, sections, image, date, dateLabel, author, readingTime, hideTitle, isFront, terms, tags, classes, pageLinks, comments, breadcrumbs, native, reload. Posts add adjacent; projects add project and nextProject; products add product. |
archive | archiveType (blog, projects, products, search, archive), eyebrow, title, description, items (cards), filters, activeFilter, pagination, query, shop. |
404 | title, text. |
With Elementor, builder holds the page’s Elementor content and Theme Builder header and footer, plus layout (canvas or full).
add_filter( 'kinetra_payload', function ( $payload ) {
if ( 'single' === $payload['kind'] && 'post' === $payload['postType'] ) {
$payload['readingTime'] = max( 1, $payload['readingTime'] - 1 );
$payload['sponsor'] = get_post_meta( $payload['id'], 'sponsor', true );
}
return $payload;
} );
To see the payload of any page, open it with ?_kinetra=1 added to the address. Use ?_kinetra=boot to get the site data as well.
Full page loads
Some content needs a real page load: its scripts only start on DOMContentLoaded, or it prints its own assets. Kinetra already does this for pages with Contact Form 7, WPForms, Gravity Forms, Fluent Forms, Ninja Forms, Formidable, Mailchimp for WP, Everest Forms and Forminator, and for WooCommerce cart, checkout and account pages. Add your own with kinetra_needs_full_load:
add_filter( 'kinetra_needs_full_load', function ( $needs, $post ) {
return $needs || has_shortcode( $post->post_content, 'my_booking_widget' );
}, 10, 2 );
In your own HTML, add data-native to a link to open it with a normal page load. Links with a target other than _self, with download, to files, to wp-admin, wp-json, feeds and to other sites always load normally.
JavaScript in the browser
Page changes don’t reload the page, so scripts that set themselves up on page load run only once. Use these events to run your code again:
| Event | Fired on | When | detail |
|---|---|---|---|
kinetra:navigate | window | After the app has moved to a new page and updated the address and title. | { url, title } |
kinetra:content | document | Each time WordPress HTML (normal blocks, shortcodes, embeds) is put on the page, including the first load. | { root }, the element that holds it |
Kinetra also triggers jQuery’s post-load event on document.body (used by Jetpack and many lazy loaders) and starts Contact Form 7, WPForms and Gravity Forms in new content. The class nx-ready appears on <html> once the app has started.
// Run a widget library on new content.
document.addEventListener('kinetra:content', function (e) {
e.detail.root.querySelectorAll('.my-slider').forEach(function (el) {
MySlider.init(el);
});
});
// Send a page view to a tool that doesn't track history changes.
window.addEventListener('kinetra:navigate', function (e) {
if (window.plausible) plausible('pageview', { u: e.detail.url });
});
// Wait for the app before measuring layout.
function whenReady(fn) {
var html = document.documentElement;
if (html.classList.contains('nx-ready')) return fn();
new MutationObserver(function (m, o) {
if (html.classList.contains('nx-ready')) { o.disconnect(); fn(); }
}).observe(html, { attributes: true, attributeFilter: ['class'] });
}
Google Analytics 4 (Kinetra’s own setting or Site Kit) needs nothing extra: the title is updated before the history change, and GA4 counts history changes as page views by default.
Snippets in Kinetra → Custom Code are printed on the first page load only. A snippet that must run on every page should listen for
kinetra:navigate.
Hooks reference
Every filter and action Kinetra provides. Theme hooks run when the Kinetra theme is active; Kinetra Core hooks run when the plugin is active.
Design and settings
| Hook | Type | Arguments | Use it to |
|---|---|---|---|
kinetra_option_schema | Filter | $schema | Add, change or remove Customizer options. |
kinetra_presets | Filter | $presets | Add design presets or change their defaults. |
kinetra_palettes | Filter | $palettes | Add colour combinations. |
kinetra_fonts | Filter | $fonts | Add Google Fonts (spec is the css2 axis string). |
kinetra_i18n | Filter | $labels | Change the frontend’s button and screen-reader labels, such as menu or skip. |
kinetra_languages | Filter | $languages | Change the language switcher items (code, name, url, current). |
Pages and content
| Hook | Type | Arguments | Use it to |
|---|---|---|---|
kinetra_payload | Filter | $payload | Change or add to the data sent to the app for a page. |
kinetra_pre_content_sections | Filter | null, $post | Supply a page’s sections yourself. |
kinetra_block_schemas | Filter | $schemas | Change section fields and defaults (blocks and Elementor widgets). Core. |
kinetra_needs_full_load | Filter | $needs, $post | Make a page open with a normal page load. |
kinetra_wc_native_page | Filter | $native | Let WooCommerce render the current page itself (default: cart, checkout, account; with classic templates also shop and products). |
SEO
| Hook | Type | Arguments | Use it to |
|---|---|---|---|
kinetra_seo_data | Filter | $seo | Change the title, description, canonical, image or noindex for the current page. Used for the head tags and for in-app navigation. Core. |
kinetra_schema_graph | Filter | $graph | Add or change JSON-LD nodes (WebSite, Person or Organization, BreadcrumbList…). Core. |
kinetra_faq_schema | Filter | true | Return false to stop FAQPage data for FAQ sections. Core. |
add_filter( 'kinetra_schema_graph', function ( $graph ) {
$graph[] = array(
'@type' => 'LocalBusiness',
'name' => get_bloginfo( 'name' ),
'telephone' => '+44 20 0000 0000',
);
return $graph;
} );
Forms and email
| Hook | Type | Arguments | Use it to |
|---|---|---|---|
kinetra_contact_submitted | Action | $fields (name, email, message, type), $sent | Save messages to a CRM, Slack, a help desk… Runs after the email is attempted. Core. |
kinetra_newsletter_signup | Action | $email | Send signups to Mailchimp, Brevo, ConvertKit and so on. Core. |
kinetra_store_subscribers | Filter | true | Return false to stop saving signups in Kinetra → Subscribers. Core. |
add_action( 'kinetra_newsletter_signup', function ( $email ) {
wp_remote_post( 'https://api.example-mail.com/v1/subscribers', array(
'headers' => array( 'Authorization' => 'Bearer ' . MY_MAIL_API_KEY ),
'body' => array( 'email' => $email ),
) );
} );
add_filter( 'kinetra_store_subscribers', '__return_false' );
Speed, caching and scripts
| Hook | Type | Arguments | Use it to |
|---|---|---|---|
kinetra_purge_caches | Action | none | Clear your own cache when Kinetra clears caches (Customizer save, demo import, custom code save, theme switch). |
kinetra_settings_changed | Action | none | Runs when Kinetra settings outside the Customizer change, such as Custom Code. Core. |
kinetra_force_asset_proxy | Filter | false | Return true to serve the app’s files through PHP instead of copying them to uploads (for read-only file systems). |
kinetra_disable_custom_code | Filter | false | Return true to stop Custom Code snippets, for example on a page you are debugging. Core. |
kinetra_core_cleanup_head | Filter | true | Return false to keep the emoji script, generator tag and other legacy head links. Core. |
kinetra_consent_tool_active | Filter | bool | Tell Kinetra a cookie-consent tool that blocks scripts is active (Complianz is detected automatically). Core. |
kinetra_dev_origin | Filter | $origin | The origin allowed to read the JSON endpoint during frontend development. |
add_action( 'kinetra_purge_caches', function () {
if ( function_exists( 'my_cdn_purge_all' ) ) {
my_cdn_purge_all();
}
} );
Uploads
| Hook | Type | Arguments | Use it to |
|---|---|---|---|
kinetra_svg_capability | Filter | 'edit_others_posts' | The capability needed to upload SVG files. Core. |
kinetra_svg_elements | Filter | $elements | The SVG elements kept when an upload is cleaned. Core. |
Demos, plugins and setup
| Hook | Type | Arguments | Use it to |
|---|---|---|---|
kinetra_demos | Filter | $demos | Add a demo to the Setup Wizard (label, tagline, description, preview, optional plugins). |
kinetra_demo_package | Filter | null, $id | Return the content of your demo as an array. |
kinetra_demo_server | Filter | $url | Load demo packages from {url}/{id}.json. |
kinetra_demo_imported | Action | $demo | Run your own setup after an import. |
kinetra_plugins | Filter | $plugins | Change the Kinetra → Plugins list (name, file, source of repo or bundled, package, required, group, description). |
kinetra_core_package | Filter | $path | Where the bundled kinetra-core.zip is. |
Add your own demo
Agencies can ship their own starter site in the Setup Wizard. Register it, then return its package:
add_filter( 'kinetra_demos', function ( $demos ) {
$demos['studio'] = array(
'label' => 'Our studio starter',
'tagline' => 'Agency · clean',
'description' => 'Our standard client site.',
'preview' => plugins_url( 'preview.svg', __FILE__ ),
);
return $demos;
} );
add_filter( 'kinetra_demo_package', function ( $package, $id ) {
if ( 'studio' !== $id ) {
return $package;
}
return array(
'id' => 'studio',
'site' => array( 'name' => 'Studio', 'tagline' => 'Design and build' ),
'settings' => array( 'style' => 'collective', 'accent' => '#FF5A1F' ),
'pages' => array(
array(
'title' => 'Home',
'slug' => 'home',
'role' => 'front',
'meta' => array( 'hide_title' => '1' ),
'content' => '<!-- wp:kinetra/hero {"title":"We make\\nthings move."} /-->',
),
),
'projects' => array(),
'posts' => array(),
'menus' => array( 'primary' => array( array( 'title' => 'Home', 'page' => 'home' ) ) ),
);
}, 10, 2 );
Page role can be front (set as the home page) or blog (set as the posts page). settings takes option keys from the schema. Look at kinetra/inc/demo/*.php for full examples with projects, posts, products and images.
REST API
| Endpoint | Body | Notes |
|---|---|---|
POST /wp-json/kinetra/v1/contact | name, email (required), message, type, consent | Sends the email to the Contact Form recipient. Five messages per IP per hour. Returns { ok, message }. |
POST /wp-json/kinetra/v1/subscribe | email (required) | Saves the signup and fires kinetra_newsletter_signup. Five per IP per hour. |
GET /any-page/?_kinetra=1 | none | The payload of that page as JSON. Sent with X-Robots-Tag: noindex and a 60-second public cache for visitors. |
Both POST endpoints have a hidden website field as a spam trap: if it is filled in, the request looks successful but nothing is sent.
Elementor
- Each section is an Elementor widget named
kinetra-{section}in the Kinetra Sections category, built from the same schema as the block. - Elementor pages, and Elementor Pro Theme Builder single, archive, 404, header and footer templates, are rendered by Elementor and placed inside the app.
- The Theme Builder header sits in
.nx-builder-headerand the footer in.nx-builder-footer. Kinetra addskx-sticky,kx-scrolledandkx-hiddento the header from the Header & Menu settings, so style against those instead of using Elementor Pro’s sticky option. - When Elementor Pro (or Pro Elements) is active, Kinetra creates “Kinetra Header” and “Kinetra Footer” templates from the Customizer settings and keeps them updated until you edit them in Elementor. They show on the site once their Display Conditions are set to Entire Site.
- One Elementor kit per preset (“Kinetra — Kinetic” and so on) mirrors the Customizer’s colours and fonts as Elementor globals.
WooCommerce
- Shop, category and product pages are drawn by the app by default. With Customize → Shop → WooCommerce classic templates, or for cart, checkout and account, WooCommerce renders the page and the app places it in its layout (
#kinetra-native). All WooCommerce hooks and template overrides work on those pages. - On animated product pages, the product data comes from
kinetra_product_payload(). Change it inkinetra_payloadunderproduct. woocommerce_short_descriptionandwoocommerce_default_catalog_orderbyare respected.- Links with
add-to-cart,remove_item,wc-ajaxorcustomer-logoutin the address always load normally.
Translation
- Text domains are
kinetra(theme) andkinetra-core(plugin), with.potfiles in eachlanguagesfolder. - Customizer text options are registered as WPML strings and Polylang strings in the
Kinetracontext. - The frontend’s labels come from
kinetra_i18n(), so they are translated with the theme. - When a visitor switches language, the app does a full page load.
Changing the frontend
The Professional and Agency plans include kinetra-frontend-source.zip, the Next.js source (React 19, Framer Motion, Lenis). Work against a local WordPress site:
cd frontend
npm install
echo "NEXT_PUBLIC_WP_URL=http://your-local-wp.test" > .env.local
npm run dev
Allow the dev server in that site’s wp-config.php:
define( 'KINETRA_DEV_ORIGIN', 'http://localhost:3000' );
| Folder | What is in it |
|---|---|
src/components/ | App shell, routing (App.tsx), header, footer, page transitions, effects, Elementor integration. |
src/sections/ | One component per section. index.tsx maps section names to components in BLOCKS. |
src/views/ | Single, archive, search, 404 and shop views. |
src/lib/ | Payload types (types.ts), fetching (api.ts), motion presets, cart, labels. |
app/globals.css | All styles, built on the --nx-* variables. |
To add a section: add its schema with kinetra_block_schemas, write a component that takes { data }, add it to BLOCKS under the same name, then build. npm run build exports the app and copies it into kinetra/app/; Kinetra publishes the new build to uploads on the next visit.
A theme update replaces
kinetra/app/with the official build. If you ship a changed frontend, put it in a child theme’s own copy of the theme, or rebuild after every update, and keep your changes in version control.
Debugging checklist
- Kinetra → Dashboard → System status shows the app build, uploads folder, Kinetra Core and SEO system.
- Open the page with
?_kinetra=1to see exactly what the app receives. - View the page source: the plain HTML in
#kinetra-ssris what search engines read. - If the page shows plain text for a few seconds and then the plain version, the app didn’t start. Check the browser console and optimizer plugins first.
- Turn off Custom Code with
add_filter( 'kinetra_disable_custom_code', '__return_true' );to rule it out. - Clear caches after changing PHP filters; payloads are cached for 60 seconds for visitors.