spring-boot-admin-docs/src/site/docs/09-samples/70-sample-custom-ui.md
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.
Location: spring-boot-admin-samples/spring-boot-admin-sample-custom-ui/
Features:
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>
cd spring-boot-admin-samples/spring-boot-admin-sample-custom-ui
mvn clean package
Build Process:
npm ci to install dependenciesnpm run build to compile Vue componentsMETA-INF/spring-boot-admin-server-ui/extensions/custom/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",
},
});
},
});
SBA.viewRegistry.addView({
name: "customSub",
parent: "custom", // Parent view name
path: "/customSub",
component: customSubitem,
label: "Custom Sub",
order: 1000,
});
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
},
});
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>`
);
<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:
SBA.useApplicationStore() for application datasba-panel, sba-status, sba-tag)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 dropdownspring-boot-admin-server-ui moduleconst { applications } = SBA.useApplicationStore();
// applications is reactive
// Contains: { name, instances[], buildVersion, status, ... }
const instance = await SBA.getInstanceById(instanceId);
const health = await instance.fetchHealth();
const metrics = await instance.fetchMetrics();
const info = await instance.fetchInfo();
SBA.eventBus.on('event-name', (data) => {
// Handle event
});
SBA.eventBus.emit('custom-event', { foo: 'bar' });
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
{
"scripts": {
"build": "vite build"
},
"dependencies": {
"vue": "^3.x"
}
}
export default {
build: {
lib: {
entry: 'src/index.js',
formats: ['es'],
fileName: 'index'
},
rollupOptions: {
external: ['vue'], // Vue provided by SBA
output: {
globals: {
vue: 'Vue'
}
}
}
}
}
<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>
Edit Vue components in src/:
<template>
<div>My custom view</div>
</template>
mvn clean package
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-sample-custom-ui</artifactId>
</dependency>
mvn spring-boot:run
Navigate to your custom view in the UI.
viewRegistry.addView({
name: "dashboard",
path: "/dashboard",
component: CustomDashboard,
label: "Dashboard",
order: 1, // First in menu
});
<template>
<sba-button @click="restartInstance">
Restart
</sba-button>
</template>
<script>
export default {
props: ['instance'],
methods: {
async restartInstance() {
await this.instance.restart();
}
}
}
</script>
<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>
✅ 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