Back to Spring Boot Admin

Custom UI Sample

spring-boot-admin-docs/src/site/docs/09-samples/70-sample-custom-ui.md

4.0.48.9 KB
Original Source

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):

xml
<!-- In servlet sample -->
<dependency>
    <groupId>de.codecentric</groupId>
    <artifactId>spring-boot-admin-sample-custom-ui</artifactId>
</dependency>

Building

bash
cd spring-boot-admin-samples/spring-boot-admin-sample-custom-ui
mvn clean package

Build Process:

  1. Frontend Maven Plugin installs Node.js
  2. Runs npm ci to install dependencies
  3. Runs npm run build to compile Vue components
  4. Copies dist files to META-INF/spring-boot-admin-server-ui/extensions/custom/
  5. Admin Server auto-loads extensions from this location

Custom View Examples

1. Top-Level View

javascript
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

javascript
SBA.viewRegistry.addView({
  name: "customSub",
  parent: "custom",          // Parent view name
  path: "/customSub",
  component: customSubitem,
  label: "Custom Sub",
  order: 1000,
});

3. Instance Endpoint View

javascript
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

javascript
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

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-ui module

Available Stores and APIs

ApplicationStore

javascript
const { applications } = SBA.useApplicationStore();

// applications is reactive
// Contains: { name, instances[], buildVersion, status, ... }

Instance API

javascript
const instance = await SBA.getInstanceById(instanceId);
const health = await instance.fetchHealth();
const metrics = await instance.fetchMetrics();
const info = await instance.fetchInfo();

Event Bus

javascript
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

json
{
  "scripts": {
    "build": "vite build"
  },
  "dependencies": {
    "vue": "^3.x"
  }
}

vite.config.js

javascript
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)

xml
<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/:

vue
<template>
  <div>My custom view</div>
</template>

2. Build

bash
mvn clean package

3. Use in Application

xml
<dependency>
    <groupId>de.codecentric</groupId>
    <artifactId>spring-boot-admin-sample-custom-ui</artifactId>
</dependency>

4. Run & Test

bash
mvn spring-boot:run

Navigate to your custom view in the UI.

Common Use Cases

Custom Dashboard

javascript
viewRegistry.addView({
  name: "dashboard",
  path: "/dashboard",
  component: CustomDashboard,
  label: "Dashboard",
  order: 1,  // First in menu
});

Instance Action Button

vue
<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

vue
<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

See Also