Custom UI Sample
Demonstrates how to create custom UI extensions for Spring Boot Admin using Vue.js components. This sample shows how to add custom views, menu items, and instance-specific endpoints to the Admin UI.
Overview
Location: spring-boot-admin-samples/spring-boot-admin-sample-custom-ui/
Features:
- Custom top-level navigation views
- Custom instance endpoint views
- Submenu integration
- Internationalization (i18n)
- Custom icons and handles
- Vue 3 components
- Access to SBA global components
- ApplicationStore integration
Prerequisites
- Java 17+, Maven 3.6+
- Node.js and npm (for building UI)
Project Structure
This is a library module that gets included by other samples (like servlet sample):
<!-- In servlet sample -->
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-sample-custom-ui</artifactId>
</dependency>
Building
cd spring-boot-admin-samples/spring-boot-admin-sample-custom-ui
mvn clean package
Build Process:
- Frontend Maven Plugin installs Node.js
- Runs
npm cito install dependencies - Runs
npm run buildto compile Vue components - Copies dist files to
META-INF/spring-boot-admin-server-ui/extensions/custom/ - Admin Server auto-loads extensions from this location
Custom View Examples
1. Top-Level View
src/index.js
SBA.use({
install({ viewRegistry, i18n }) {
viewRegistry.addView({
name: "custom", // Unique view name
path: "/custom", // URL path
component: custom, // Vue component
group: "custom", // Group for styling
handle, // Custom navigation handle
order: 1000, // Menu order
});
// Add translations
i18n.mergeLocaleMessage("en", {
custom: {
label: "My Extensions",
},
});
i18n.mergeLocaleMessage("de", {
custom: {
label: "Meine Erweiterung",
},
});
},
});
2. Submenu Item
SBA.viewRegistry.addView({
name: "customSub",
parent: "custom", // Parent view name
path: "/customSub",
component: customSubitem,
label: "Custom Sub",
order: 1000,
});
3. Instance Endpoint View
SBA.viewRegistry.addView({
name: "instances/custom",
parent: "instances", // Under instance views
path: "custom",
component: customEndpoint,
label: "Custom",
group: "custom",
order: 1000,
isEnabled: ({ instance }) => {
return instance.hasEndpoint("custom"); // Conditional rendering
},
});
4. Custom Group Icon
SBA.viewRegistry.setGroupIcon(
"custom",
`<svg xmlns='http://www.w3.org/2000/svg' class='h-5 mr-3' viewBox='0 0 576 512'>
<path d='M512 80c8.8 0 16 7.2 16 16V416c0...'/>
</svg>`
);
Vue Component Example
src/custom.vue
<template>
<div class="m-4">
<template v-for="application in applications" :key="application.name">
<sba-panel :title="application.name">
This application has the following instances:
<ul>
<template v-for="instance in application.instances">
<li>
<span class="mx-1" v-text="instance.registration.name"></span>
<!-- SBA components are registered globally -->
<sba-status :status="instance.statusInfo.status" class="mx-1" />
<sba-tag :value="instance.id" class="mx-1" label="id" />
</li>
</template>
</ul>
</sba-panel>
</template>
</div>
</template>
<script>
export default {
setup() {
const { applications } = SBA.useApplicationStore(); // Access store
return {
applications,
};
},
};
</script>
Key Features:
- Access to
SBA.useApplicationStore()for application data - Global SBA components (
sba-panel,sba-status,sba-tag) - Reactive data from Vuex store
- Tailwind CSS classes for styling
Available SBA Components
Global components you can use without importing:
<sba-panel>- Card/panel container<sba-status>- Status indicator (UP/DOWN/etc.)<sba-tag>- Tag display<sba-icon>- Icon component<sba-button>- Button component<sba-input>- Input field<sba-toggle>- Toggle switch<sba-instance-selector>- Instance dropdown- Many more in
spring-boot-admin-server-uimodule
Available Stores and APIs
ApplicationStore
const { applications } = SBA.useApplicationStore();
// applications is reactive
// Contains: { name, instances[], buildVersion, status, ... }
Instance API
const instance = await SBA.getInstanceById(instanceId);
const health = await instance.fetchHealth();
const metrics = await instance.fetchMetrics();
const info = await instance.fetchInfo();
Event Bus
SBA.eventBus.on('event-name', (data) => {
// Handle event
});
SBA.eventBus.emit('custom-event', { foo: 'bar' });
File Structure
spring-boot-admin-sample-custom-ui/
├── src/
│ ├── index.js # Main entry point
│ ├── custom.vue # Top-level view component
│ ├── custom-subitem.vue # Submenu component
│ ├── custom-endpoint.vue # Instance endpoint component
│ ├── handle.vue # Navigation handle component
│ └── custom.css # Custom styles
├── package.json
├── vite.config.js
└── pom.xml
Build Configuration
package.json
{
"scripts": {
"build": "vite build"
},
"dependencies": {
"vue": "^3.x"
}
}
vite.config.js
export default {
build: {
lib: {
entry: 'src/index.js',
formats: ['es'],
fileName: 'index'
},
rollupOptions: {
external: ['vue'], // Vue provided by SBA
output: {
globals: {
vue: 'Vue'
}
}
}
}
}
pom.xml (Maven Integration)
<plugin>
<groupId>com.github.eirslett</groupId>
<artifactId>frontend-maven-plugin</artifactId>
<executions>
<execution>
<id>install-node-and-npm</id>
<goals>
<goal>install-node-and-npm</goal>
</goals>
</execution>
<execution>
<id>npm-build</id>
<goals>
<goal>npm</goal>
</goals>
<configuration>
<arguments>run build</arguments>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<executions>
<execution>
<id>copy-resources</id>
<phase>process-resources</phase>
<goals>
<goal>copy-resources</goal>
</goals>
<configuration>
<outputDirectory>
${project.build.outputDirectory}/META-INF/spring-boot-admin-server-ui/extensions/custom
</outputDirectory>
<resources>
<resource>
<directory>${project.build.directory}/dist</directory>
</resource>
</resources>
</configuration>
</execution>
</executions>
</plugin>
Development Workflow
1. Make Changes
Edit Vue components in src/:
<template>
<div>My custom view</div>
</template>
2. Build
mvn clean package
3. Use in Application
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-sample-custom-ui</artifactId>
</dependency>
4. Run & Test
mvn spring-boot:run
Navigate to your custom view in the UI.
Common Use Cases
Custom Dashboard
viewRegistry.addView({
name: "dashboard",
path: "/dashboard",
component: CustomDashboard,
label: "Dashboard",
order: 1, // First in menu
});
Instance Action Button
<template>
<sba-button @click="restartInstance">
Restart
</sba-button>
</template>
<script>
export default {
props: ['instance'],
methods: {
async restartInstance() {
await this.instance.restart();
}
}
}
</script>
Custom Metrics View
<template>
<div>
<h2>CPU Usage: {{ cpuUsage }}%</h2>
</div>
</template>
<script>
export default {
props: ['instance'],
data() {
return { cpuUsage: 0 };
},
async mounted() {
const metrics = await this.instance.fetchMetrics();
this.cpuUsage = metrics['process.cpu.usage'] * 100;
}
}
</script>
Key Takeaways
✅ Full Customization: Add any Vue component to UI ✅ SBA Integration: Access stores, components, APIs ✅ Maven Integration: Build with Maven ✅ Reusable: Package as library for multiple projects
Next Steps
- UI Customization Guide
- Servlet Sample (uses this extension)