uiXpress
Api

Toolbar Render API

Inject Vue or React components into the admin toolbar with flexible positioning and capability-based rendering

Overview

The Toolbar Render API uses filter hooks similar to the Dashboard Cards API and Sidebar Render API, allowing plugins to register components that will be rendered in specific locations within the toolbar. Components can be written in either Vue.js or React, with automatic integration via veaury.

Architecture

  • Framework Support: Vue.js 3 and React (via veaury)
  • Filter Hooks: WordPress-style filter system
  • Component Renderer: Reusable component renderer handles both Vue and React
  • Event System: Custom events for plugin registration timing
  • Default Components: Existing toolbar buttons (Screen Options, Help, View Site/Dashboard) are registered via the API

Filter Hooks

Hook Names

  • uixpress/toolbar/render/left - Components rendered on the left side (before logo/WpToolBar)
  • uixpress/toolbar/render/right - Components rendered on the right side (after search, before notifications)

Hook Type

Filter Hook - Modifies the components array

Hook Signature

addFilter('uixpress/toolbar/render/left', (components) => {
  // Modify components array
  return components;
}, priority);

addFilter('uixpress/toolbar/render/right', (components) => {
  // Modify components array
  return components;
}, priority);

Parameters

  • components (Array): The current array of component objects
  • priority (Number, optional): Priority for the filter (default: 10). Lower numbers run first.

Return Value

Returns an array of component objects.

Component Object Structure

Each component must be an object with the following structure:

{
  metadata: {
    id: string,                    // Unique identifier (required)
    language: string,              // 'vue' or 'react' (optional, default: 'vue')
    className: string,            // CSS classes to apply (optional)
    requires_capabilities: Array,  // User capabilities required (optional)
  },
  component: Component            // Vue or React component (required)
}

Metadata Properties

id (string, required)

  • Unique identifier for the component
  • Used as React key for Vue components
  • Should be namespaced (e.g., my-plugin/toolbar-widget)
  • Default toolbar buttons use IDs: toolbar-screen-options, toolbar-help, toolbar-view-site

language (string, optional)

  • Component language: 'vue' or 'react'
  • Default: 'vue'
  • React components are automatically wrapped with veaury

className (string, optional)

  • CSS classes to apply to the component wrapper
  • Useful for styling and layout

requires_capabilities (Array, optional)

  • Array of WordPress capability strings
  • Component only renders if user has at least one capability
  • Example: ['manage_options', 'edit_posts']

Component Property

component (Component, required)

  • The Vue or React component to render
  • For Vue: Standard Vue component
  • For React: React component (will be wrapped automatically)

Default Toolbar Components

The following components are registered by default via the API:

  1. Screen Options (toolbar-screen-options)
    • Toggles WordPress screen options panel
    • Only visible if screen options are available
    • Respects hide_screenoptions setting
  2. Help (toolbar-help)
    • Toggles WordPress contextual help panel
    • Only visible if contextual help is available
    • Respects hide_help_toggle setting
  3. View Site/Dashboard (toolbar-view-site)
    • Shows "View Site" button when in admin
    • Shows "View Dashboard" button when on frontend
    • Links to site URL or admin URL accordingly

These components can be removed or modified using filter hooks (see examples below).

Usage Examples

Registering a Vue Component (Right Side)

import { addFilter } from '@/assets/js/functions/HooksSystem.js';
// Or use window.uixpress.addFilter if outside Vue app

import MyToolbarComponent from './my-toolbar-component.vue';

addFilter('uixpress/toolbar/render/right', (components) => {
  return [
    ...components,
    {
      metadata: {
        id: 'my-plugin/toolbar-widget',
        language: 'vue',
        className: '',
        requires_capabilities: ['manage_options'],
      },
      component: MyToolbarComponent,
    },
  ];
});

Registering a React Component (Left Side)

import { addFilter } from '@/assets/js/functions/HooksSystem.js';
import MyLeftToolbarComponent from './MyLeftToolbarComponent.jsx';

addFilter('uixpress/toolbar/render/left', (components) => {
  return [
    ...components,
    {
      metadata: {
        id: 'my-plugin/left-toolbar-widget',
        language: 'react',
        className: 'mr-4',
      },
      component: MyLeftToolbarComponent,
    },
  ];
});

Removing Default Components

import { addFilter } from '@/assets/js/functions/HooksSystem.js';

// Remove Screen Options button
addFilter('uixpress/toolbar/render/right', (components) => {
  return components.filter(
    (component) => component.metadata?.id !== 'toolbar-screen-options'
  );
});

// Remove Help button
addFilter('uixpress/toolbar/render/right', (components) => {
  return components.filter(
    (component) => component.metadata?.id !== 'toolbar-help'
  );
});

// Remove View Site button
addFilter('uixpress/toolbar/render/right', (components) => {
  return components.filter(
    (component) => component.metadata?.id !== 'toolbar-view-site'
  );
});

Modifying Default Components

import { addFilter } from '@/assets/js/functions/HooksSystem.js';

// Modify View Site button to add custom class
addFilter('uixpress/toolbar/render/right', (components) => {
  return components.map((component) => {
    if (component.metadata?.id === 'toolbar-view-site') {
      return {
        ...component,
        metadata: {
          ...component.metadata,
          className: 'custom-class',
        },
      };
    }
    return component;
  });
});

Vue Component Example

<script setup>
import { defineProps } from 'vue';
import { useAppStore } from '@/store/app/app.js';
import AppButton from '@/components/utility/app-button/index.vue';
import AppIcon from '@/components/utility/icons/index.vue';

const props = defineProps({
  appData: Object,
  id: String,
  className: String,
});

const appStore = useAppStore();

const handleClick = () => {
  // Your custom action
  console.log('Toolbar button clicked');
};
</script>

<template>
  <AppButton
    type="transparent"
    :title="__('My Custom Button', 'uixpress')"
    @click="handleClick"
    :class="className"
  >
    <AppIcon icon="settings" class="text-xl" />
  </AppButton>
</template>

React Component Example

import React from 'react';

/**
 * Toolbar React component
 * @param {Object} props - Component props
 * @param {Object} props.appData - App store instance
 * @param {string} props.id - Component ID
 * @param {string} props.className - CSS classes
 */
const MyToolbarComponent = ({ appData, id, className }) => {
  const handleClick = () => {
    // Your custom action
    console.log('Toolbar button clicked');
  };

  return (
    <button
      onClick={handleClick}
      className={`px-3 py-2 rounded hover:bg-zinc-100 dark:hover:bg-zinc-800 ${className || ''}`}
      title="My Custom Button"
    >
      <span className="text-xl">⚙️</span>
    </button>
  );
};

export default MyToolbarComponent;

Conditional Registration

import { addFilter } from '@/assets/js/functions/HooksSystem.js';

addFilter('uixpress/toolbar/render/right', (components) => {
  const newComponents = [...components];

  // Only add component if user has specific capability
  if (window.uixpress?.state?.currentUser?.allcaps?.manage_options) {
    newComponents.push({
      metadata: {
        id: 'my-plugin/admin-toolbar-button',
        language: 'vue',
        requires_capabilities: ['manage_options'],
      },
      component: AdminOnlyComponent,
    });
  }

  return newComponents;
});

Using Priority to Control Order

import { addFilter } from '@/assets/js/functions/HooksSystem.js';

// High priority - runs first, adds component at the beginning
addFilter(
  'uixpress/toolbar/render/right',
  (components) => {
    return [
      {
        metadata: {
          id: 'my-plugin/important-button',
          language: 'vue',
        },
        component: ImportantComponent,
      },
      ...components,
    ];
  },
  5 // Low priority number = runs first
);

// Low priority - runs last, adds component at the end
addFilter(
  'uixpress/toolbar/render/right',
  (components) => {
    return [
      ...components,
      {
        metadata: {
          id: 'my-plugin/less-important-button',
          language: 'vue',
        },
        component: LessImportantComponent,
      },
    ];
  },
  20 // High priority number = runs last
);

Component Props

Components receive the following props:

Vue Components

  • appData (Object): The app store instance (Pinia store)
  • id (String): Component ID from metadata
  • className (String): CSS classes from metadata

React Components

  • appData (Object): The app store instance (Pinia store)
  • id (String): Component ID from metadata
  • className (String): CSS classes from metadata

Event System

The toolbar component dispatches a custom event when it's ready for plugin registration:

document.addEventListener('uixpress/toolbar/ready', () => {
  // Toolbar component is ready, safe to register components
  addFilter('uixpress/toolbar/render/right', (components) => {
    // Register your components
    return components;
  });
});

Render Locations

Left Side Components

Components registered via uixpress/toolbar/render/left are rendered:

  • Before the logo/WpToolBar section
  • On the left side of the toolbar
  • Useful for custom branding or navigation

Right Side Components

Components registered via uixpress/toolbar/render/right are rendered:

  • After the search component
  • Before the notifications component
  • Before the user avatar/menu
  • Default buttons (Screen Options, Help, View Site) are registered here

Best Practices

1. Namespace Your Component IDs

Always prefix your component IDs with your plugin/theme name:

{
  metadata: {
    id: 'my-plugin/my-toolbar-button', // Good
    // Not: 'my-button' // Bad - could conflict with default components
  }
}

2. Use Appropriate Styling

Follow the design system:

  • Use zinc color palette for backgrounds and borders
  • Support dark mode with dark: classes
  • Match existing toolbar button styling
  • Use AppButton component for consistency

3. Handle Capabilities Gracefully

Check user capabilities before rendering sensitive content:

{
  metadata: {
    requires_capabilities: ['manage_options'], // Only admins see this
  }
}

4. Keep Components Lightweight

Toolbar components should be lightweight and fast-loading:

  • Avoid heavy computations
  • Use lazy loading for large components
  • Optimize images and assets

5. Test Both Vue and React

If supporting both frameworks:

  • Test React components thoroughly
  • Ensure props are passed correctly
  • Verify veaury wrapping works as expected

6. Respect Existing Settings

Default components respect uiXpress settings:

  • hide_screenoptions - Hides Screen Options button
  • hide_help_toggle - Hides Help button
  • disable_search - Hides Search component

When creating custom components, consider similar settings or respect existing ones.

Integration with WordPress Plugins

PHP Plugin Integration

If you're building a WordPress plugin, you can enqueue JavaScript that registers components:

add_action('admin_enqueue_scripts', function() {
    wp_enqueue_script(
        'my-plugin-toolbar-components',
        plugin_dir_url(__FILE__) . 'assets/toolbar-components.js',
        ['uixpress-app'], // Depend on uiXpress app
        '1.0.0',
        true
    );
});

Then in your JavaScript file:

// Wait for toolbar to be ready
document.addEventListener('uixpress/toolbar/ready', () => {
  if (window.uixpress?.addFilter) {
    window.uixpress.addFilter('uixpress/toolbar/render/right', (components) => {
      return [
        ...components,
        {
          metadata: {
            id: 'my-plugin/my-toolbar-button',
            language: 'vue',
            requires_capabilities: ['manage_options'],
          },
          component: MyComponent, // Imported or defined elsewhere
        },
      ];
    });
  }
});

Troubleshooting

Components Not Appearing

  1. Check Hook Registration: Ensure you're using addFilter correctly
  2. Check Event Timing: Register filters after uixpress/toolbar/ready event
  3. Check Capabilities: Verify user has required capabilities
  4. Check Console: Look for JavaScript errors that might prevent registration
  5. Check Component ID: Ensure component ID is unique and doesn't conflict

React Components Not Rendering

  1. Check Language Property: Ensure metadata.language is set to 'react'
  2. Check veaury: Verify veaury is properly configured
  3. Check Props: React components receive appData, id, className (camelCase)
  4. Check Console: Look for React rendering errors

Vue Components Not Rendering

  1. Check Component Export: Ensure component is properly exported
  2. Check Props: Vue components receive app-data, id, className (kebab-case)
  3. Check Console: Look for Vue rendering errors

Default Components Still Showing After Removal

  1. Check Filter Priority: Ensure your filter runs after default registration (use higher priority number)
  2. Check Component ID: Verify you're filtering by the correct ID
  3. Check Filter Logic: Ensure filter returns modified array correctly

API Reference

Filter Hooks

Name: uixpress/toolbar/render/left

Type: Filter

Parameters:

  • components (Array): Current components array

Returns: Array of component objects

Priority: Default 10 (lower runs first)


Name: uixpress/toolbar/render/right

Type: Filter

Parameters:

  • components (Array): Current components array

Returns: Array of component objects

Priority: Default 10 (lower runs first)

Event

Name: uixpress/toolbar/ready

Type: CustomEvent

When: Dispatched when toolbar component is mounted and ready for plugin registration

Usage: Listen for this event before registering filters

Default Component IDs

  • toolbar-screen-options - Screen Options button
  • toolbar-help - Help button
  • toolbar-view-site - View Site/Dashboard button