uiXpress
Api

Sidebar Render API

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

Overview

The Sidebar Render API uses filter hooks similar to the Dashboard Cards API, allowing plugins to register components that will be rendered in specific locations within the sidebar. 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

Filter Hooks

Hook Names

  • uixpress/sidebar/render/premenu - Components rendered before the menu items
  • uixpress/sidebar/render/postmenu - Components rendered after the menu items, before user details

Hook Type

Filter Hook - Modifies the components array

Hook Signature

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

addFilter('uixpress/sidebar/render/postmenu', (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/premenu-widget)

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)

Usage Examples

Registering a Vue Component (Pre-menu)

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

import MyPreMenuComponent from './my-premenu-component.vue';

addFilter('uixpress/sidebar/render/premenu', (components) => {
  return [
    ...components,
    {
      metadata: {
        id: 'my-plugin/premenu-widget',
        language: 'vue',
        className: 'p-4',
        requires_capabilities: ['manage_options'],
      },
      component: MyPreMenuComponent,
    },
  ];
});

Registering a React Component (Post-menu)

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

addFilter('uixpress/sidebar/render/postmenu', (components) => {
  return [
    ...components,
    {
      metadata: {
        id: 'my-plugin/postmenu-widget',
        language: 'react',
        className: 'border-t border-zinc-200 dark:border-zinc-800 pt-4',
      },
      component: MyPostMenuComponent,
    },
  ];
});

Vue Component Example

<script setup>
import { defineProps } from 'vue';
import { useAppStore } from '@/store/app/app.js';

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

const appStore = useAppStore();
</script>

<template>
  <div :class="className" class="p-4 bg-zinc-50 dark:bg-zinc-800 rounded-lg">
    <h3 class="text-sm font-semibold text-zinc-900 dark:text-zinc-100 mb-2">
      My Custom Widget
    </h3>
    <p class="text-xs text-zinc-600 dark:text-zinc-400">
      This component appears before the menu items.
    </p>
  </div>
</template>

React Component Example

import React from 'react';

/**
 * Post-menu 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 MyPostMenuComponent = ({ appData, id, className }) => {
  return (
    <div className={`p-4 bg-zinc-50 dark:bg-zinc-800 rounded-lg ${className || ''}`}>
      <h3 className="text-sm font-semibold text-zinc-900 dark:text-zinc-100 mb-2">
        My Custom Widget
      </h3>
      <p className="text-xs text-zinc-600 dark:text-zinc-400">
        This component appears after the menu items.
      </p>
    </div>
  );
};

export default MyPostMenuComponent;

Conditional Registration

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

addFilter('uixpress/sidebar/render/premenu', (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-widget',
        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/sidebar/render/premenu',
  (components) => {
    return [
      {
        metadata: {
          id: 'my-plugin/important-widget',
          language: 'vue',
        },
        component: ImportantComponent,
      },
      ...components,
    ];
  },
  5 // Low priority number = runs first
);

// Low priority - runs last, adds component at the end
addFilter(
  'uixpress/sidebar/render/premenu',
  (components) => {
    return [
      ...components,
      {
        metadata: {
          id: 'my-plugin/less-important-widget',
          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 sidebar component dispatches a custom event when it's ready for plugin registration:

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

Render Locations

Pre-menu Components

Components registered via uixpress/sidebar/render/premenu are rendered:

  • Before shortcuts/favorites section
  • At the top of the sidebar menu area
  • Visible when menu is not minimized

Post-menu Components

Components registered via uixpress/sidebar/render/postmenu are rendered:

  • After all menu items
  • Before the user details section
  • Visible when menu is not minimized

Best Practices

1. Namespace Your Component IDs

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

{
  metadata: {
    id: 'my-plugin/my-component', // Good
    // Not: 'my-component' // Bad - could conflict
  }
}

2. Use Appropriate Styling

Follow the design system:

  • Use zinc color palette for backgrounds and borders
  • Support dark mode with dark: classes
  • Use consistent spacing (p-4, gap-2, etc.)
  • Match existing sidebar styling

3. Handle Capabilities Gracefully

Check user capabilities before rendering sensitive content:

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

4. Keep Components Lightweight

Sidebar 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

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-sidebar-components',
        plugin_dir_url(__FILE__) . 'assets/sidebar-components.js',
        ['uixpress-app'], // Depend on uiXpress app
        '1.0.0',
        true
    );
});

Then in your JavaScript file:

// Wait for sidebar to be ready
document.addEventListener('uixpress/sidebar/ready', () => {
  if (window.uixpress?.addFilter) {
    window.uixpress.addFilter('uixpress/sidebar/render/premenu', (components) => {
      return [
        ...components,
        {
          metadata: {
            id: 'my-plugin/my-widget',
            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/sidebar/ready event
  3. Check Capabilities: Verify user has required capabilities
  4. Check Console: Look for JavaScript errors that might prevent registration
  5. Check Menu State: Components only render when menu is not minimized

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

API Reference

Filter Hooks

Name: uixpress/sidebar/render/premenu

Type: Filter

Parameters:

  • components (Array): Current components array

Returns: Array of component objects

Priority: Default 10 (lower runs first)


Name: uixpress/sidebar/render/postmenu

Type: Filter

Parameters:

  • components (Array): Current components array

Returns: Array of component objects

Priority: Default 10 (lower runs first)

Event

Name: uixpress/sidebar/ready

Type: CustomEvent

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

Usage: Listen for this event before registering filters