Firefox DevTools Custom Object Formatters

Posted by

Firefox 116 introduces an exciting new feature called custom formatters, designed to enhance the debugging experience for web developers. With custom formatters, websites can now define how to display specific JavaScript objects and functions within different parts of the DevTools. Custom Formatters have been part of Chrome DevTools for a while and we’re happy to add support for them in Firefox. The implementation of this feature was funded by Clojurists Together. So a big thank you to them!

This is especially useful for JavaScript frameworks that define individual objects or create wrappers for native variables, as well as web applications that deal with complex object structures. That also covers frameworks that compile to JavaScript like ClojureScript.

Custom formatting enhances the debugging process by providing developers with a more intuitive and informative representation of their objects. When working with complex object structures, the default display of objects in the DevTools can often be overwhelming or cryptic. With custom formatters, developers can tailor the presentation of objects to match their specific needs. They can highlight important properties by applying formatting styles, filter out irrelevant information, or even display nested structures in a more readable manner. This enables developers to quickly identify and understand the state of their objects, reducing the time and effort required for debugging and ultimately leading to more efficient development workflows.

Enabling custom formatting

To enable custom formatting, switch to the Settings panel and check the option called “Enable custom formatters” under “Advanced settings”. The setting takes effect the next time you open the DevTools.

Settings panel showing the option for custom formatters being enabled

Controlling variable display

Once custom formatters are enabled, websites can customize how objects are displayed in the Web Console and the Debugger.

This is achieved by defining custom formatters using the global array called devtoolsFormatters. Each entry in this array represents a specific formatter for a particular type of variable. If there’s no formatter defined for a variable, it is displayed using its default formatting.

Each formatter must at least contain a header function. This function must either return a JsonML array or null. If null is returned, the default format is used to display the object.

In addition to the header function, a formatter can contain a body. Whether a formatter has a body is indicated by the hasBody function. If it returns true, the object can be expanded to show more details. The actual body is then returned by the body function. Like the header function it can either return a JsonML object or null.

All three functions take the object as first parameter an an optional configuration object as second parameter, which allows to pass additional information.

For a more detailed understanding of how custom formatters are created and the various options available, refer to the Firefox DevTools User Docs.

Examples

Let’s take a look at a simple example to illustrate the concept of custom formatters:

Output of simple custom formatter within the Web Console.

And the code that achieves that output can be written as:

window.devtoolsFormatters = [
  {
    header: variable => {
      if (variable.hasOwnProperty('foo')) {
        return [
          'span', {
            'style': `
              font-family: "Comic Sans MS", fantasy;
              font-size: 3rem;
              color: green;
            `
          },
          'foo'
        ];
      }
      return null;
    }
  }
];

// …

console.log({foo: "bar"});

In the example above, a custom formatter is defined for a variable. The header property of the formatter is a function that determines how the variable is displayed. In this case, if the variable has a property named foo, it will be rendered as a <span> element with a specific style.

Complete example

Custom formatters can also be very complex and include nested structures. Here is an example showing how a Date object could be displayed. The code for that could look like this:

window.devtoolsFormatters = [
  {
    header: (obj) => {
      if (obj instanceof Date) {
        return [
          "div",
          { style: "font-weight: bold;" },
          `Date: ${obj.toLocaleDateString()} ${obj.toLocaleTimeString()}`,
        ];
      }
      return null;
    },
    hasBody: (obj) => obj instanceof Date,
    body: (obj) => {
      if (obj instanceof Date) {
        return [
          "div",
          {},
          ["div", {}, `Year: ${obj.getFullYear()}`],
          ["div", {}, `Month: ${obj.getMonth() + 1}`],
          ["div", {}, `Day: ${obj.getDate()}`],
          ["div", {}, `Hour: ${obj.getHours()}`],
          ["div", {}, `Minutes: ${obj.getMinutes()}`],
          ["div", {}, `Seconds: ${obj.getSeconds()}`],
        ];
      }
      return null;
    },
  },
];

// …

console.log(new Date());

Feel free to try it out by copying the code.

With this custom formatter, a Date object logged to the console will look like this:

Date object logged via console.log(new Date()) using a custom formatter

Restrictions

There are some restrictions for HTML elements and CSS styles that can be applied to them.

These are the allowed HTML elements:

<span>, <div>, <ol>, <ul>, <li>, <table>, <tr>, <td>

The CSS properties that can be applied to the elements are:

  • align*
  • background* (background-image only allows data: URLs)
  • border*
  • box*
  • clear
  • color
  • cursor
  • display
  • float
  • font*
  • justify*
  • line*
  • margin*
  • padding*
  • position (only the values static and relative are accepted)
  • text*
  • transition*
  • outline*
  • vertical-align
  • white-space
  • word*
  • writing*
  • width, min-width, max-width
  • height, min-height, max-height

Debugging custom formatters

If a custom formatter contains an error, an error is logged to the console explaining what’s wrong.

Error message logged to the Web Console for an invalid custom formatter

If possible, it also contains a source link pointing to the exact location of the error within the code. There are some errors like a missing body function, though, which can’t provide a source link. In those cases, the error message should still provide enough information to let you know in which custom formatter the error occurred and what the error was.

In addition to that you can also add breakpoints to your custom formatters within the Debugger. This allows step-debugging their code like any other JavaScript code.

Note that the same APIs are supported within Chromium based browsers, which means that these formatters function cross-browser, despite the lack of a specification.

Existing formatters

There is a bunch of existing formatters covering different needs:

Conclusion

With the introduction of custom formatters in Firefox DevTools, web developers now have more control over how objects are displayed during debugging sessions. So, when dealing with complex objects that may benefit from a more streamlined or prioritized display of information, custom formatters may come in handy. Feel free to give this feature a try and let us know what you think about it or whether you experience any issues using it!

The feature was implemented by me in cooperation with Nicolas Chevobbe. As Firefox and the DevTools are open source, they actively encourage contributions from volunteers in the development community.

Leave a Reply

Your email address will not be published. Required fields are marked *