The user details view uses a tabbed interface where components can be registered to specific categories (tabs). Components are self-contained Vue or React components that receive user data and handle their own logic. The system supports both Vue.js 3 and React components, allowing developers to choose their preferred framework.
Each component consists of three main files. The component file extension depends on the framework you choose:
Vue Component Structure:
your-component/
├── metadata.js # Component configuration
├── component.vue # Vue component
└── index.js # Export file
React Component Structure:
your-component/
├── metadata.js # Component configuration
├── component.jsx # React component
└── index.js # Export file
metadata.jsexport default {
id: 'unique-component-id',
title: 'Component Title',
category: 'details', // 'details', 'activity', 'commerce', or custom
language: 'vue', // 'vue' or 'react'
requires_capabilities: ['edit_users'], // Optional
};
component.vue (Vue Component)<script setup>
import { ref, computed, onMounted, watch } from 'vue';
const props = defineProps({
userId: {
type: [String, Number],
required: true,
},
userData: {
type: Object,
required: true,
},
appData: {
type: Object,
required: true,
},
id: {
type: String,
required: true,
},
});
// Your component logic here
</script>
<template>
<!-- Your component template -->
</template>
component.jsx (React Component)import React, { useState, useEffect } from 'react';
/**
* Your React Component
*
* @param {Object} props - Component props
* @param {string|number} props.userId - The user ID
* @param {Object} props.userData - Complete user data object
* @param {Object} props.appData - Vue app store instance
* @param {string} props.id - Component ID from metadata
*/
const YourComponent = ({ userId, userData, appData, id }) => {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(false);
useEffect(() => {
// Load data when component mounts
loadData();
}, [userId]);
const loadData = async () => {
setLoading(true);
try {
// Your API call here
// const response = await fetch(...);
// setData(response);
} catch (error) {
console.error('Error loading data:', error);
} finally {
setLoading(false);
}
};
return (
<div className="bg-zinc-50 dark:bg-zinc-800/20 border border-zinc-200/40 dark:border-zinc-800/60 rounded-xl p-6">
{/* Your component content */}
</div>
);
};
export default YourComponent;
index.jsFor Vue Components:
import metadata from './metadata.js';
import component from './component.vue';
export default {
metadata,
component,
};
For React Components:
import metadata from './metadata.js';
import component from './component.jsx';
export default {
metadata,
component,
};
| Property | Type | Description | Default |
|---|---|---|---|
id | string | Unique identifier for the component | Required |
title | string | Display title for the component | Required |
category | string | Tab category (e.g., 'details', 'activity', 'commerce') | Required |
language | string | Component language ('vue' or 'react') | 'vue' |
| Property | Type | Description |
|---|---|---|
requires_capabilities | array | WordPress capabilities required to view the component |
Vue Component Example:
export default {
id: 'user-custom-fields',
title: 'Custom Fields',
category: 'details',
language: 'vue',
requires_capabilities: ['edit_users'],
};
React Component Example:
export default {
id: 'user-social-links',
title: 'Social Links',
category: 'details',
language: 'react', // Important: Set to 'react' for React components
requires_capabilities: ['edit_users'],
};
Your component receives four props:
userId (String | Number)The ID of the user being viewed.
Vue Example:
const userId = props.userId; // "123" or 123
React Example:
const userId = userId; // "123" or 123
userData (Object)Complete user data object from WordPress REST API:
{
id: number,
name: string,
email: string,
slug: string,
avatar_urls: object,
registered_date: string,
roles: array,
capabilities: object,
// ... other WordPress user fields
}
Vue Example:
const userName = props.userData.name;
const userEmail = props.userData.email;
React Example:
const userName = userData.name;
const userEmail = userData.email;
appData (Object)The Vue app store instance containing:
{
state: {
adminUrl: string, // WordPress admin URL
siteURL: string, // Site URL
currentUser: object, // Current user data
uixpress_settings: object, // Plugin settings
// ... other app state
}
}
id (String)The component ID from metadata, useful for unique identifiers or debugging.
Note for React Components: Veaury automatically converts Vue's kebab-case props (user-id, user-data, app-data) to React's camelCase (userId, userData, appData). However, it's recommended to handle both formats for maximum compatibility:
const userId = props.userId || props['user-id'];
const userData = props.userData || props['user-data'];
const appData = props.appData || props['app-data'];
<script setup>
import { ref, computed, onMounted, watch } from 'vue';
import { lmnFetch } from '@/assets/js/functions/lmnFetch.js';
const props = defineProps({
userId: { type: [String, Number], required: true },
userData: { type: Object, required: true },
appData: { type: Object, required: true },
});
// Component state
const data = ref([]);
const loading = ref(false);
const error = ref(null);
// Load data function
const loadData = async () => {
loading.value = true;
try {
const args = {
endpoint: `wp/v2/users/${props.userId}/custom-endpoint`,
};
const response = await lmnFetch(args);
data.value = response.data;
} catch (err) {
error.value = err.message;
} finally {
loading.value = false;
}
};
// Watch for user ID changes
watch(
() => props.userId,
() => {
loadData();
}
);
// Watch for user data changes
watch(
() => props.userData,
() => {
// React to user data changes
},
{ deep: true }
);
// Load data on mount
onMounted(() => {
loadData();
});
</script>
For components within the UIXpress plugin:
// In app/src/pages/users/src/components/index.js
import { addFilter } from '@/assets/js/functions/HooksSystem.js';
import myComponent from './my-component/index.js';
addFilter('uixpress/user-details/components/register', (components) => {
return [...components, myComponent];
});
For external plugins, listen for the uixpress/user-details/ready event and use the global window.uixpress API:
// In your external plugin
document.addEventListener('uixpress/user-details/ready', () => {
const { addFilter } = window.uixpress;
addFilter('uixpress/user-details/components/register', (components) => {
return [...components, myExternalComponent];
});
});
Categories define the tabs available in the user details view. You can register custom categories:
import { addFilter } from '@/assets/js/functions/HooksSystem.js';
addFilter('uixpress/user-details/categories/register', (categories) => {
return [
...categories,
{ value: 'custom-tab', label: __('Custom Tab', 'uixpress') },
];
});
The following categories are registered by default:
details - User details and form fieldsactivity - User activity log (only shown if activity logger is enabled)commerce - Commerce-related data (e.g., WooCommerce customer data)Categories can be conditionally displayed based on settings or plugin availability. For example, the activity category only appears when the activity logger is enabled, and the commerce category only appears when WooCommerce is active.
Control component visibility based on user permissions:
export default {
id: 'admin-only-component',
title: 'Admin Only',
category: 'details',
language: 'vue',
requires_capabilities: ['manage_options'],
};
export default {
id: 'editor-component',
title: 'Editor Component',
category: 'details',
language: 'vue',
requires_capabilities: ['edit_users', 'manage_options'],
};
If the user has any of the specified capabilities, the component will be visible.
The component-render component uses Vue's useAttrs() to pass through any additional props or attributes to your component. This means you can pass custom props when registering components:
// In your component registration
const myComponent = {
metadata: {
id: 'custom-component',
title: 'Custom Component',
category: 'details',
},
component: MyComponent,
};
// The component will receive all props passed to ComponentRender
// metadata.js
export default {
id: 'user-custom-info',
title: 'Custom Information',
category: 'details',
language: 'vue',
};
<!-- component.vue -->
<script setup>
import { ref, onMounted } from 'vue';
import { lmnFetch } from '@/assets/js/functions/lmnFetch.js';
const props = defineProps({
userId: { type: [String, Number], required: true },
userData: { type: Object, required: true },
appData: { type: Object, required: true },
});
const customData = ref(null);
const loading = ref(false);
const loadCustomData = async () => {
loading.value = true;
try {
const args = {
endpoint: `custom/v1/user/${props.userId}/info`,
};
const response = await lmnFetch(args);
customData.value = response.data;
} catch (error) {
console.error('Failed to load custom data:', error);
} finally {
loading.value = false;
}
};
onMounted(() => {
loadCustomData();
});
</script>
<template>
<div class="bg-white dark:bg-zinc-900 rounded-xl p-6 border border-zinc-200 dark:border-zinc-800">
<h3 class="text-lg font-semibold mb-4">Custom Information</h3>
<div v-if="loading" class="text-center py-4">
<p class="text-sm text-zinc-500">Loading...</p>
</div>
<div v-else-if="customData" class="space-y-3">
<div v-for="(value, key) in customData" :key="key" class="flex justify-between">
<span class="text-sm text-zinc-500">{{ key }}:</span>
<span class="text-sm font-medium">{{ value }}</span>
</div>
</div>
<div v-else class="text-center py-4">
<p class="text-sm text-zinc-500">No custom data available</p>
</div>
</div>
</template>
metadata.js:
export default {
id: 'user-social-profile',
title: 'Social Profile',
category: 'details',
language: 'react', // Important: Set to 'react'
};
component.jsx:
import React, { useState, useEffect } from 'react';
/**
* User Social Profile Component
*
* @param {Object} props - Component props
* @param {string|number} props.userId - The user ID
* @param {Object} props.userData - Complete user data object
* @param {Object} props.appData - Vue app store instance
*/
const UserSocialProfile = (props) => {
// Handle both camelCase and kebab-case prop names (veaury compatibility)
const userId = props.userId || props['user-id'];
const userData = props.userData || props['user-data'];
const appData = props.appData || props['app-data'];
const [socialLinks, setSocialLinks] = useState(null);
const [loading, setLoading] = useState(false);
useEffect(() => {
loadSocialLinks();
}, [userId]);
const loadSocialLinks = async () => {
setLoading(true);
try {
// Your API call here
// const response = await fetch(...);
// setSocialLinks(response);
} catch (error) {
console.error('Error loading social links:', error);
} finally {
setLoading(false);
}
};
return (
<div className="bg-white dark:bg-zinc-900 rounded-xl p-6 border border-zinc-200 dark:border-zinc-800">
<h3 className="text-lg font-semibold mb-4">Social Profile</h3>
{loading ? (
<div className="text-center py-4">
<p className="text-sm text-zinc-500">Loading...</p>
</div>
) : (
<div className="space-y-3">
{/* Your social links content */}
</div>
)}
</div>
);
};
export default UserSocialProfile;
Vue Component Example:
// In your external plugin
(function () {
'use strict';
// Wait for UIXpress user details to be ready
document.addEventListener('uixpress/user-details/ready', function (event) {
const { addFilter } = window.uixpress;
const { userId, userData } = event.detail;
// Define your Vue component
const myExternalComponent = {
metadata: {
id: 'my-plugin-component',
title: 'My Plugin Data',
category: 'details',
language: 'vue',
requires_capabilities: ['edit_users'],
},
component: {
// Your Vue component definition
template: `
<div class="bg-white dark:bg-zinc-900 rounded-xl p-6">
<h3>My Plugin Component</h3>
<p>User ID: {{ userId }}</p>
<p>User Name: {{ userData.name }}</p>
</div>
`,
props: ['userId', 'userData', 'appData', 'id'],
},
};
// Register the component
addFilter('uixpress/user-details/components/register', function (components) {
return [...components, myExternalComponent];
});
});
})();
React Component Example:
// In your external plugin
(function () {
'use strict';
// Wait for UIXpress user details to be ready
document.addEventListener('uixpress/user-details/ready', function (event) {
const { addFilter } = window.uixpress;
const { userId, userData } = event.detail;
// Define your React component
const MyReactComponent = ({ userId, userData, appData, id }) => {
return (
<div className="bg-white dark:bg-zinc-900 rounded-xl p-6">
<h3>My React Plugin Component</h3>
<p>User ID: {userId}</p>
<p>User Name: {userData.name}</p>
</div>
);
};
// Define your React component registration
const myExternalReactComponent = {
metadata: {
id: 'my-react-plugin-component',
title: 'My React Plugin Data',
category: 'details',
language: 'react', // Important: Set to 'react'
requires_capabilities: ['edit_users'],
},
component: MyReactComponent,
};
// Register the component
addFilter('uixpress/user-details/components/register', function (components) {
return [...components, myExternalReactComponent];
});
});
})();
// Add debugging to your component
console.log('User ID:', props.userId);
console.log('User Data:', props.userData);
console.log('App Data:', props.appData);
console.log('Component ID:', props.id);
For more information, visit the UIXpress Documentation or contact support.