Native JSON UI for Plugins
Bedrock Server Manager allows plugins to define native UI pages using a simple JSON schema. This eliminates the need for plugin developers to write frontend code (React, HTML, CSS) while still providing a rich, interactive user interface that matches the application’s look and feel.
How it Works
Instead of serving HTML or Jinja2 templates, your plugin defines a FastAPI route that returns a JSON response. This route is tagged with plugin-ui-native. The frontend detects this tag and renders the JSON using a dynamic component renderer.
Global Server Context
The Web UI features a global server selector in the sidebar. When a user navigates to your plugin’s Native UI page, the frontend automatically appends the currently selected server name to the URL as a query parameter (e.g., ?server=survival_server).
Your FastAPI route should accept this server query parameter to dynamically fetch and display data for the chosen server. If the user changes the server in the dropdown, your UI route will be re-fetched with the new server parameter.
Example:
@self.router.get("/my_plugin/ui", response_class=JSONResponse, tags=["plugin-ui-native"])
async def get_ui(server: str = "default_server"): # Accept the server query param
# Fetch data specific to the selected server using the Core API
status = self.api.get_server_running_status(server)
is_running = status.get("running", False)
return JSONResponse(content={
"type": "Card",
"props": {"title": f"Status for {server}"},
"children": [
{
"type": "Badge",
"props": {
"content": "Online" if is_running else "Offline",
"variant": "success" if is_running else "danger"
}
}
]
})
Basic Example
Here is a minimal example of a plugin that adds a native UI page:
from fastapi import APIRouter, Request
from fastapi.responses import JSONResponse
from bedrock_server_manager import PluginBase
class MyPlugin(PluginBase):
def on_load(self, **kwargs):
self.router = APIRouter(tags=["My Plugin"])
@self.router.get(
"/my_plugin/ui",
response_class=JSONResponse,
name="My Plugin UI",
tags=["plugin-ui-native"] # <--- This tag enables the Native UI renderer
)
async def get_ui(request: Request):
return JSONResponse(content={
"type": "Container",
"children": [
{
"type": "Text",
"props": {"content": "Hello from Native UI!", "variant": "h1"}
},
{
"type": "Card",
"props": {"title": "My Card"},
"children": [
{"type": "Text", "props": {"content": "This is content inside a card."}}
]
}
]
})
def get_fastapi_routers(self, **kwargs):
return [self.router]
Available Components
The JSON schema supports a wide range of components. Each component is an object with type, props, and optional children.
Layout
Container: Main wrapper.
Card: Boxed container with an optional
title.Row: Horizontal layout.
Column: Vertical layout.
Divider: Horizontal line separator.
Typography
Text: Renders text.
content: The text to display.variant:h1,h2,h3, orbody.
Basic Inputs
Input: Text input.
Select: Dropdown menu.
Switch: Toggle switch.
Checkbox: Checkbox input.
Button: Clickable button.
label: Text on the button.icon: Name of the Lucide icon (e.g., “Save”, “Play”).variant:primary,secondary,danger.onClickAction: Action definition (see Actions below).
FileUpload: File selection input.
Display & Media
Badge: Status label.
content: Text.variant:primary,secondary,success,danger,warning.
CodeBlock: Displays code or logs with a copy button.
content: The code/text.title: Optional header.
Image: Displays an image.
Link: External or internal link.
iframe: Embeds external content.
Interactive
Accordion: Collapsible section.
Modal: Dialog popup.
id: Unique ID to control visibility.title: Header text.children: Content inside the modal.
Data & Monitoring
Table: Displays tabular data.
StatCard: Dashboard metric card.
label: Title of the metric.value: The value to display.icon: Icon name.trend:up,down, orneutral.
StatusIndicator: Dot with status text.
status:running(green),stopped(red),warning(orange).
LogViewer: Scrolling log display.
lines: Array of log strings.height: Height in pixels (default 200).
Chart: Data visualization (Area, Line, Bar).
type:area,line,bar.data: Array of data objects.xAxis: Key for the X-axis (e.g., “time”).series: Array of objects defining lines/bars (dataKey,color,name).
Actions
Components like Button can trigger actions via the onClickAction prop.
API Call
Sends a POST request to a backend endpoint.
{
"type": "api_call",
"endpoint": "/api/my_plugin/submit",
"includeFormState": true, // Sends current input values
"refresh": true, // Refreshes the UI after success
"closeModal": true // Closes any open modal
}
Modal Control
Opens or closes a modal by ID.
{
"type": "open_modal",
"modalId": "my_modal_id"
}
{
"type": "close_modal"
}
Real-time Updates (WebSockets)
You can subscribe to WebSocket topics to update components like Charts, StatCards, and LogViewers in real-time.
Define Subscriptions: Add
websocketSubscriptionsto the root of your JSON response.{ "websocketSubscriptions": ["my_plugin:stats", "server_log:{server}"], "type": "Container" }
{server}is automatically replaced with the currently selected server name.
Bind Components: Pass
socketTopicto components.Chart: Appends new data points from the socket message to the chart history.
LogViewer: Appends new log lines.
StatCard: Updates the value. Use
dataKeyto specify which field in the socket message to display (e.g.,process_info.cpu_percent).
{ "type": "StatCard", "props": { "label": "CPU", "socketTopic": "my_plugin:stats", "dataKey": "cpu_usage" } }
Icons
The system uses Lucide React icons. You can use any valid Lucide icon name in props like icon. Common icons include: Activity, AlertCircle, CheckCircle2, Info, Terminal, Save, Trash2, Plus, X, Upload, Download, Play, Square, RotateCcw.
Comprehensive Examples
Form and API Submission
This example creates a simple form inside a Card and a Save button that submits the form data to an API endpoint. The includeFormState: true prop ensures all input values on the page are sent in the POST request body.
{
"type": "Container",
"children": [
{
"type": "Card",
"props": {
"title": "Plugin Settings"
},
"children": [
{
"type": "Input",
"props": {
"name": "api_key",
"label": "External API Key",
"type": "password",
"placeholder": "Enter your key..."
}
},
{
"type": "Switch",
"props": {
"name": "enable_feature_x",
"label": "Enable Feature X",
"defaultChecked": true
}
},
{
"type": "Button",
"props": {
"label": "Save Configuration",
"icon": "Save",
"variant": "primary",
"onClickAction": {
"type": "api_call",
"endpoint": "/api/plugins/my_plugin/save_settings",
"method": "POST",
"includeFormState": true,
"refresh": true
}
}
}
]
}
]
}
Table View
Displaying data in a table with customizable columns and rows.
{
"type": "Card",
"props": {
"title": "Recent Actions"
},
"children": [
{
"type": "Table",
"props": {
"columns": [
{ "header": "User", "accessor": "user" },
{ "header": "Action", "accessor": "action" },
{ "header": "Timestamp", "accessor": "timestamp" },
{ "header": "Status", "accessor": "status" }
],
"data": [
{ "user": "admin", "action": "Server Start", "timestamp": "2023-10-27 10:00", "status": "Success" },
{ "user": "mod_1", "action": "Config Update", "timestamp": "2023-10-27 11:30", "status": "Success" },
{ "user": "admin", "action": "Backup Run", "timestamp": "2023-10-27 12:00", "status": "Failed" }
]
}
}
]
}
Real-Time Monitoring Dashboard
A combination of layout components (Row, Column) and real-time components (StatCard, LogViewer) bound to WebSocket topics. Notice the root websocketSubscriptions array.
{
"websocketSubscriptions": [
"server_process_stats:{server}",
"server_log:{server}"
],
"type": "Container",
"children": [
{
"type": "Text",
"props": {
"content": "Live Server Telemetry",
"variant": "h2"
}
},
{
"type": "Row",
"children": [
{
"type": "Column",
"children": [
{
"type": "StatCard",
"props": {
"label": "CPU Usage",
"value": "0%",
"icon": "Cpu",
"socketTopic": "server_process_stats:{server}",
"dataKey": "cpu_percent_str",
"trend": "neutral"
}
}
]
},
{
"type": "Column",
"children": [
{
"type": "StatCard",
"props": {
"label": "Memory",
"value": "0 MB",
"icon": "MemoryStick",
"socketTopic": "server_process_stats:{server}",
"dataKey": "memory_str"
}
}
]
}
]
},
{
"type": "Card",
"props": {
"title": "Live Console"
},
"children": [
{
"type": "LogViewer",
"props": {
"socketTopic": "server_log:{server}",
"lines": ["--- Waiting for logs ---"],
"height": 300
}
}
]
}
]
}