Shuffleboard Custom Plugin MapType Properties



I am trying to make a custom Widget in shuffleboard and am using the built in Map datatype. My problem is when I try to export the properties of my widget. When I added the export properties line, the widget no longer shows up in the widget tab, but I can right click on a tree in the network tables and it will say “show as field observer widget” but I can’t actually click on it.

When I remove the export properties line, the widget shows up in shuffleboard and I can place it just fine. But obviously the properties don’t work.

Here is my widget class:


Your project is using last year’s API. Change your pom.xml file to use version 2019.2.1 of the shuffleboard API.

You also don’t need to manually specify JavaFX or ntcore dependencies - those are handled by Maven as transitive dependencies of the shuffleboard API library.

The MapData data type should really only be used for handling unknown generic data since widgets can’t specify a schema (which is what complex data types really are). Better to create a custom data type and data class (eg RobotPose and RobotPoseType) that encapsulate the data and allow shuffleboard to perform validation.


Ah ok. I had that originally and was confused as how to actually use that type from our robot. Would you set the widget type using .add.withWidget? How does the robot know the types that are available.


If you’re dealing with composite data, it should be encapsulated in a Sendable type in robot code. The plugin should have a data class representing that data.

You don’t need to specify withWidget in robot code if the plugin specifies the default component to use for that data type - which you should be doing anyway for custom data.


Custom Sendable

public class RobotPose extends SendableBase {
  private double x, y, heading;
  public RobotPose() {
    super(false); // Do not send to LiveWindow

  // Getters and setters for x, y, heading...

  public void initSendable(SendableBuilder builder) {
    builder.addDoubleProperty("x", () -> x, null);
    // Similarly for y, heading ...

Adding to a shuffleboard tab

RobotPose pose = new RobotPose();

  .add("Robot Pose", pose);


In your shuffleboard plugin, you would have an immutable data type for the robot pose data (as opposed to the mutable type in robot code, which is required for a sendable):

RobotPose data class

public final class RobotPose extends ComplexData<RobotPose> {
  private final double x, y, heading;

  public RobotPose(double x, double y, double heading) {
    this.x = x;
    this.y = y;
    this.heading = heading;

  // Getters for x, y, heading...

  public Map<String, Object> asMap() {
    return Map.of("x", x, "y", y, "heading", heading);

  public String toHumanReadableString() {
    return String.format("(%.1f, %.1f), %.3f degrees", x, y, heading);

Specifying the default widget

public Map<DataType, ComponentType> getDefaultComponents() {
  return Map.of(RobotPoseType.Instance, WidgetType.forAnnotatedWidget(FieldObserver.class));


Thank you for the help. I will definitely work on implementing the above code. I really appreciate what you are doing with Shuffleboard and love how customizable it is.



Happy to help!


One more question for you:

Is there a way to simulate a custom data type from the outline viewer? I don’t have a robot in front of me and because of the nature of my plugin it would be useful to simulate data.



I typically just run a small Java WPILib program on my computer - that lets me use the same classes and methods as I’d use in a real robot program without needing to manually set entries in NetworkTables.

But if you know the entries to set in OutlineViewer, yes you can use that tool:

Key Value Type
.type The name of the data type, eg "RobotPose" String
x The X-coordinate of the robot Number
y The Y-coordinate of the robot Number
heading The heading of the robot Number

Note that the entries x, y, and heading must have the exact same name as used in the data type defined in the shuffleboard plugin - spelling, capitalization, and whitespace must all match.