Skip to content

REST API

Access theme options programmatically via the WordPress REST API.

Overview

ThemePlus exposes a full REST API under the themeplus/v1namespace that allows you to get, save, and reset theme option values programmatically — useful for headless WordPress setups, static site generators, external dashboards, or any application that needs to read or write theme configuration. The API is available as soon as ThemePlus is active.

The endpoint is registered under the themeplus/v1 namespace and is available as soon as ThemePlus is active.

Endpoint

GET /wp-json/themeplus/v1/options

Returns all saved theme options as a JSON object.

Authentication

All ThemePlus REST endpoints require authentication. Requests without valid credentials receive a 403 Forbidden response.

For browser-based requests (the ThemePlus admin panel itself), WordPress handles authentication automatically via cookie auth and the X-WP-Nonce header.

For external requests, use WordPress Application Passwords (WordPress 5.6+):

📄<code>javascript
const credentials = btoa( 'username:application_password' );

const response = await fetch( '/wp-json/themeplus/v1/options', {
    headers: {
        'Authorization': `Basic ${credentials}`,
    },
});

The required capability is edit_theme_options by default. This can be changed via themeplus_framework_config(['capability' => 'manage_options']).

MethodEndpointDescriptionAuth required
GET/wp-json/themeplus/v1/optionsGet all saved options
POST/wp-json/themeplus/v1/optionsSave all options
POST/wp-json/themeplus/v1/options/resetReset all options to defaults
POST/wp-json/themeplus/v1/options/reset-sectionReset a single section to defaults
GET/wp-json/themeplus/v1/configGet full sections and fields config
GET/wp-json/themeplus/v1/dev-panelField metadata and statistics✅ dev mode only

Response Format

A successful request returns a JSON object where each key is an option ID and each value is the saved option value — matching the same structure returned by themeplus_get_option() in PHP.

Example response

📄<code>json
{
    "primary_color": "#2271b1",
    "enable_preloader": true,
    "header_layout": "standard",
    "body_typography": {
        "font-family": "Inter",
        "font-weight": "400",
        "font-size": "16",
        "line-height": "1.6",
        "letter-spacing": "0",
        "text-transform": "none",
        "font-style": "normal",
        "subsets": ["latin"]
    },
    "logo_image": {
        "id": 42,
        "url": "https://example.com/wp-content/uploads/logo.png",
        "width": 300,
        "height": 100,
        "alt": "My Logo",
        "title": "Logo"
    },
    "social_links": [
        { "platform": "github",    "url": "https://github.com/yourhandle" },
        { "platform": "instagram", "url": "https://instagram.com/yourhandle" }
    ],
    "hero_gallery": [
        { "id": 42, "url": "https://example.com/wp-content/uploads/image-1.jpg", "alt": "" },
        { "id": 43, "url": "https://example.com/wp-content/uploads/image-2.jpg", "alt": "Second image" }
    ]
}

Usage Examples

Fetch all options with the browser Fetch API

📄<code>javascript
const response = await fetch( '/wp-json/themeplus/v1/options' );
const options  = await response.json();

console.log( options.primary_color );   // '#2271b1'
console.log( options.header_layout );  // 'standard'

Fetch a single option value

ThemePlus does not expose individual option endpoints by default. Filter the full response in JavaScript:

📄<code>javascript
async function getThemeOption( key, fallback = null ) {
    const response = await fetch( '/wp-json/themeplus/v1/options' );
    const options  = await response.json();
    return options[ key ] ?? fallback;
}

const color = await getThemeOption( 'primary_color', '#000000' );
document.documentElement.style.setProperty( '--color-primary', color );

Applying options to a headless frontend

📄<code>javascript
// Fetch theme options and inject as CSS custom properties
async function applyThemeOptions() {
	const res = await fetch('https://your-wp-site.com/wp-json/themeplus/v1/options');
	const options = await res.json();

	const root = document.documentElement;

	if (options.primary_color) root.style.setProperty('--color-primary', options.primary_color);
	if (options.secondary_color) root.style.setProperty('--color-secondary', options.secondary_color);

	if (options.body_typography) {
		const t = options.body_typography;
		if (t['font-family']) root.style.setProperty('--body-font-family', `"${t['font-family']}", sans-serif`);
		if (t['font-size']) root.style.setProperty('--body-font-size', t['font-size'] + 'px');
		if (t['line-height']) root.style.setProperty('--body-line-height', t['line-height']);
		if (t['font-weight']) root.style.setProperty('--body-font-weight', t['font-weight']);
		if (t['letter-spacing']) root.style.setProperty('--body-letter-spacing', t['letter-spacing'] + 'px');
		if (t['text-transform']) root.style.setProperty('--body-text-transform', t['text-transform']);
	}
}

applyThemeOptions();

Using with Next.js getStaticProps

📄<code>javascript
// pages/index.js
export async function getStaticProps() {
    const res     = await fetch( `${process.env.WP_API_URL}/wp-json/themeplus/v1/options` );
    const options = await res.json();

    return {
        props: { themeOptions: options },
        revalidate: 60, // ISR — revalidate every 60 seconds
    };
}

export default function Home({ themeOptions }) {
    return (
        <main style={{ '--color-primary': themeOptions.primary_color }}>
            {/* page content */}
        </main>
    );
}

Using with WP REST API authentication for private options

📄<code>javascript
// Authenticated request using Application Passwords (WordPress 5.6+)
const credentials = btoa( 'username:application_password' );

const response = await fetch( '/wp-json/themeplus/v1/options', {
    headers: {
        'Authorization': `Basic ${credentials}`,
    },
});

const options = await response.json();

Changing the Required Capability

By default all endpoints require edit_theme_options. To require a stricter capability, set it in your framework config:

📄<code>php
themeplus_framework_config([
    'capability' => 'manage_options',
]);

This affects both the admin panel visibility and all REST API endpoints — they always use the same configured capability.

Registering a Custom Endpoint

If you need a custom endpoint that returns only a subset of options — for example to expose only public-safe values — register your own REST route in functions.php:

📄<code>php
add_action( 'rest_api_init', function() {
    register_rest_route( 'mytheme/v1', '/public-options', [
        'methods'             => 'GET',
        'callback'            => function() {
            $options = themeplus_get_option();

            // Return only safe, public-facing values
            return rest_ensure_response([
                'primary_color'    => $options['primary_color']    ?? '',
                'secondary_color'  => $options['secondary_color']  ?? '',
                'social_links'     => $options['social_links']     ?? [],
                'header_layout'    => $options['header_layout']    ?? 'standard',
            ]);
        },
        'permission_callback' => '__return_true',
    ]);
});
GET /wp-json/mytheme/v1/public-options

Notes

  • The REST API returns values exactly as stored — Image fields return a structured array { id, url, width, height, alt, title } and Gallery fields return an array of { id, url, alt } rows. URLs are already resolved — no ID-to-URL conversion is needed in your frontend.
  • Social Media fields return an array of { platform, url }rows — not an object keyed by platform name.
  • For headless setups, consider caching the options response at the CDN or using ISR to avoid hitting the WordPress REST API on every request.
  • REST API access requires WordPress permalinks to be set to anything other than Plain — go to Settings → Permalinks and save if /wp-json/ routes return 404.
  • For headless setups, consider caching the options response at the CDN or using ISR (Incremental Static Regeneration) to avoid hitting the WordPress REST API on every request.
  • The endpoint returns all options including any that may contain user-generated content — always audit what your options contain before making the endpoint publicly accessible.
  • REST API access requires WordPress permalinks to be set to anything other than Plain — go to Settings → Permalinks and save if /wp-json/ routes return 404.

On This Page