Back to Tutorials
Mastering Custom Renderers - Part 1

Mastering Custom Renderers: Part 1 - Fundamentals

A
Alex Morgan
August 5, 2025
Advanced
25 minutes
#renderer#customization#advanced#tutorial
Mastering Custom Renderers: Part 1 - Fundamentals

Mastering Custom Renderers: Part 1 - Fundamentals

In this multi-part tutorial series, we'll take a deep dive into creating custom renderers for Blockly. This first installment covers the fundamentals of the rendering system and how to create your first custom renderer.

Understanding the Renderer Architecture

Since Blockly 3.0, the rendering system has been completely modular, making it possible to entirely customize how blocks appear in your application. Before diving into code, let's understand the architecture:

  1. Renderer: The top-level class that orchestrates the rendering process
  2. Renderer Constants: Configuration values that control block appearances
  3. Measurables: Classes that calculate and store block dimensions
  4. Drawer: Classes that draw the actual SVG elements

The default renderers included with Blockly are:

  • Geras: The classic Blockly look with beveled edges
  • Thrasos: A modern, flat design style
  • Zelos: A sleek, minimalist style with different block shapes

Setting Up Your Development Environment

First, let's set up a basic Blockly environment where we can develop our custom renderer:

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <title>Custom Renderer Development</title>
  <script src="https://unpkg.com/blockly/blockly.min.js"></script>
  <style>
    #blocklyDiv {
      height: 600px;
      width: 800px;
    }
  </style>
</head>
<body>
  <div id="blocklyDiv"></div>
  <script>
    // We'll add our renderer code here
    const workspace = Blockly.inject('blocklyDiv', {
      toolbox: document.getElementById('toolbox')
    });
  </script>
  <xml id="toolbox" style="display: none">
    <block type="controls_if"></block>
    <block type="logic_compare"></block>
    <block type="controls_repeat_ext"></block>
    <block type="math_number">
      <field name="NUM">123</field>
    </block>
    <block type="text"></block>
  </xml>
</body>
</html>

Creating Your First Custom Renderer

Now, let's create a simple custom renderer by extending the base Blockly renderer:

// Step 1: Create a custom constants provider
class MyRendererConstants extends Blockly.blockRendering.ConstantProvider {
  constructor() {
    super();
    
    // Override constants to customize block appearance
    this.NOTCH_WIDTH = 20; // Wider notches
    this.NOTCH_HEIGHT = 10; // Taller notches
    
    // Set custom corner radius
    this.CORNER_RADIUS = 10; // Rounder corners
    
    // Custom colors
    this.FIELD_TEXT_COLOUR = '#FFFFFF';
    this.FIELD_BORDER_RECT_COLOUR = '#006699';
  }
  
  // Override the notch shape
  makeNotch() {
    const width = this.NOTCH_WIDTH;
    const height = this.NOTCH_HEIGHT;
    
    // Create a custom notch shape (a simple triangle)
    const path = `l ${width/2},${height} l ${width/2},-${height}`;
    
    return {
      type: this.SHAPES.NOTCH,
      width: width,
      height: height,
      pathLeft: path
    };
  }
}

// Step 2: Create the custom renderer
class MyRenderer extends Blockly.blockRendering.Renderer {
  constructor() {
    super('my_renderer');
  }
  
  makeConstants_() {
    return new MyRendererConstants();
  }
}

// Step 3: Register the renderer
Blockly.blockRendering.register('my_renderer', MyRenderer);

// Step 4: Use the custom renderer
const workspace = Blockly.inject('blocklyDiv', {
  toolbox: document.getElementById('toolbox'),
  renderer: 'my_renderer'
});

Understanding the Results

With this simple renderer, you should see several changes to your blocks:

  • Wider, triangular notches instead of the default puzzle-piece shape
  • Rounder corners on all blocks
  • Custom field colors with white text on a blue background

This is just scratching the surface, but it demonstrates the power of the renderer system. You can control virtually every visual aspect of your blocks.

Debugging Your Renderer

When developing custom renderers, it's helpful to visualize the block metrics. Let's add a debug helper:

function enableRendererDebug(workspace) {
  // Display block bounds and connection locations
  workspace.addChangeListener(function(e) {
    if (e.type === Blockly.Events.BLOCK_MOVE) {
      const block = workspace.getBlockById(e.blockId);
      if (block) {
        console.log('Block metrics:', {
          id: block.id,
          type: block.type,
          bounds: block.getBoundingRectangle(),
          connections: block.getConnections_().map(c => ({
            type: c.type,
            position: c.getOffsetInBlock()
          }))
        });
      }
    }
  });
}

// Enable debug mode
enableRendererDebug(workspace);

Next Steps

In Part 2 of this series, we'll explore:

  • Creating custom field renderers
  • Adding animations to block connections
  • Implementing theme-based rendering variations
  • Optimizing renderer performance

Exercises

Before moving on to Part 2, try these exercises to deepen your understanding:

  1. Modify the corner radius to create completely square blocks
  2. Change the input shapes from the default indentation to a distinct visual element
  3. Implement a custom connection shape for value inputs
  4. Create a "dark mode" variation of your renderer with appropriate colors

<CodeDemo title="Try It Yourself" height="400px" defaultCode={` // Try modifying the renderer constants here class MyRendererConstants extends Blockly.blockRendering.ConstantProvider { constructor() { super();

// Your customizations go here
this.CORNER_RADIUS = 10;
this.NOTCH_WIDTH = 20;

} } `} />

<NextStepCTA nextTutorial="mastering-custom-renderers-part-2" seriesTitle="Mastering Custom Renderers" nextTitle="Part 2 - Advanced Techniques" />

Stay Updated with New Tutorials

Get notified when we publish new tutorials, articles and learning resources.

By subscribing, you agree to our Privacy Policy. We'll send you relevant content, and you can unsubscribe at any time.