The toolbox is the place where users get blocks. Usually it is displayed on one side of the workspace. Sometimes it has categories, and sometimes it does not.
This page mainly focuses on how to specify the structure of your toolbox (i.e. what categories it has, and what blocks they contain). If you want more details about how to change the UI of your toolbox, check out the Customizing a Blockly toolbox codelab and the 2021 Toolbox APIs talk.
Formats
Blockly allows you to specify your toolbox's structure using a few different formats. The new recommended format uses JSON, and the old format uses XML.
Here are the different ways you could specify the above toolbox:
JSON
As of the September 2020 release toolboxes can be defined using JSON.
var toolbox = {
"kind": "flyoutToolbox",
"contents": [
{
"kind": "block",
"type": "controls_if"
},
{
"kind": "block",
"type": "controls_whileUntil"
}
]
};
var workspace = Blockly.inject('blocklyDiv', {toolbox: toolbox});
XML
<xml id="toolbox" style="display: none">
<block type="controls_if"></block>
<block type="controls_whileUntil"></block>
</xml>
<script>
var workspace = Blockly.inject('blocklyDiv',
{toolbox: document.getElementById('toolbox')});
</script>
XML String
var toolbox = '<xml>' +
'<block type="controls_if"></block>' +
'<block type="controls_whileUntil"></block>' +
'</xml>';
var workspace = Blockly.inject('blocklyDiv', {toolbox: toolbox});
Categories
The blocks in the toolbox may be organized in categories.
Here are the ways you could define the above toolbox, which has two categories ('Control' and 'Logic'), each of which contain blocks:
JSON
{
"kind": "categoryToolbox",
"contents": [
{
"kind": "category",
"name": "Control",
"contents": [
{
"kind": "block",
"type": "controls_if"
},
]
},
{
"kind": "category",
"name": "Logic",
"contents": [
{
"kind": "block",
"type": "logic_compare"
},
{
"kind": "block",
"type": "logic_operation"
},
{
"kind": "block",
"type": "logic_boolean"
}
]
}
]
}
XML
<xml id="toolbox" style="display: none">
<category name="Control">
<block type="controls_if"></block>
<category name="Logic">
<block type="logic_compare"></block>
<block type="logic_operation"></block>
<block type="logic_boolean"></block>
</category>
</xml>
Nested categories
Categories may be nested within other categories. Here are two top-level categories ('Core' and 'Custom'), the second of which contains two sub-categories, each of which contain blocks:
Note that it is possible for a category to contain both sub-categories and blocks. In the above example, 'Custom' has two sub-categories ('Move' and 'Turn'), as well as a block of its own ('start').
JSON
{
"kind": "categoryToolbox",
"contents": [
{
"kind": "category",
"name": "Core",
"contents": [
{
"kind": "block",
"type": "controls_if"
},
{
"kind": "block",
"type": "logic_compare"
},
]
},
{
"kind": "category",
"name": "Custom",
"contents": [
{
"kind": "block",
"type": "start"
},
{
"kind": "category",
"name": "Move",
"contents": [
{
"kind": "block",
"type": "move_forward"
}
]
},
{
"kind": "category",
"name": "Turn",
"contents": [
{
"kind": "block",
"type": "turn_left"
}
]
}
]
}
]
}
XML
<xml id="toolbox" style="display: none">
<category name="Core">
<block type="controls_if"></block>
<block type="logic_compare"></block>
</category>
<category name="Custom">
<block type="start"></block>
<category name="Move">
<block type="move_forward"></block>
</category>
<category name="Turn">
<block type="turn_left"></block>
</category>
</category>
</xml>
Dynamic categories
Dynamic categories are categories that are dynamically repopulated based on a function each time they are opened.
Blockly supports this by allowing you to associate a category with a function via a registered string key. The function should return a definition of a category's contents (including blocks, buttons, labels, etc). The contents can be specified as JSON or XML, although JSON is recommended.
Also note that the function is provided the target workspace as a parameter, so the blocks in your dynamic category can be based on the state of the workspace.
JSON
As of the September 2021 release, you can specify the state of blocks without
using 'blockxml'
.
// Returns an array of objects.
var coloursFlyoutCallback = function(workspace) {
// Returns an array of hex colours, e.g. ['#4286f4', '#ef0447']
var colourList = getPalette();
var blockList = [];
for (var i = 0; i < colourList.length; i++) {
blockList.push({
'kind': 'block',
'type': 'colour_picker',
'fields': {
'COLOUR': colourList[i]
}
});
}
return blockList;
};
// Associates the function with the string 'COLOUR_PALETTE'
myWorkspace.registerToolboxCategoryCallback(
'COLOUR_PALETTE', coloursFlyoutCallback);
Old JSON
Before the September 2021 release, you had to use the 'blockxml'
property to
specify the state of blocks.
// Returns an array of objects.
var coloursFlyoutCallback = function(workspace) {
// Returns an array of hex colours, e.g. ['#4286f4', '#ef0447']
var colourList = getPalette();
var blockList = [];
for (var i = 0; i < colourList.length; i++) {
blockList.push({
'kind': 'block',
'type': 'colour_picker', // Type is optional if you provide blockxml
'blockxml': '<block type="colour_picker">' +
'<field name="COLOUR">' + colourList[i] + '</field>' +
'</block>'
});
}
return blockList;
};
// Associates the function with the string 'COLOUR_PALETTE'
myWorkspace.registerToolboxCategoryCallback(
'COLOUR_PALETTE', coloursFlyoutCallback);
XML
// Returns an arry of XML nodes.
var coloursFlyoutCallback = function(workspace) {
// Returns an array of hex colours, e.g. ['#4286f4', '#ef0447']
var colourList = getPalette();
var blockList = [];
for (var i = 0; i < colourList.length; i++) {
var block = document.createElement('block');
block.setAttribute('type', 'colour_picker');
var field = document.createElement('field');
field.setAttribute('name', 'COLOUR');
field.innerText = colourList[i];
block.appendChild(field);
blockList.push(block);
}
return blockList;
};
// Associates the function with the string 'COLOUR_PALETTE'
myWorkspace.registerToolboxCategoryCallback(
'COLOUR_PALETTE', coloursFlyoutCallback);
After the dynamic category functions are associated with a string key (aka
registered) you can assign this string key to the custom
property of
your category definition to make the category dynamic.
JSON
{
"kind": "category",
"name": "Colours",
"custom": "COLOUR_PALETTE"
}
XML
<category name="Colours" custom="COLOUR_PALETTE"></category>
Built-in dynamic categories
Blockly provides three built-in dynamic categories.
'VARIABLE'
creates a category for untyped variables.'VARIABLE_DYNAMIC'
creates a category for typed variables. It has buttons to create strings, numbers, and colours.'PROCEDURE'
creates a category for function blocks.
JSON
{
"kind": "category",
"name": "Variables",
"custom": "VARIABLE"
},
{
"kind": "category",
"name": "Variables",
"custom": "VARIABLE_DYNAMIC"
},
{
"kind": "category",
"name": "Functions",
"custom": "PROCEDURE"
}
XML
<category name="Variables" custom="VARIABLE"></category>
<category name="Variables" custom="VARIABLE_DYNAMIC"></category>
<category name="Functions" custom="PROCEDURE"></category>
Note: The word 'procedure' is used throughout the Blockly codebase, but the word 'function' has found to be more understandable by students. Sorry for the mismatch.
Disabling
A disabled category will not allow a user to open the category and it will be skipped during keyboard navigation.
var category = toolbox.getToolboxItems()[0];
category.setDisabled('true');
When a category is disabled, a 'disabled'
property is added to the DOM element,
which allows you to control the look of a disabled category.
.blocklyToolboxCategory[disabled="true"] {
opacity: .5;
}
Hiding
A hidden category will not be shown as part of the toolbox. Hidden categories can later be shown via JavaScript.
JSON
{
"kind": "category",
"name": "...",
"hidden": "true"
}
XML
<category name="..." hidden="true"></category>
JavaScript
var category = toolbox.getToolboxItems()[0];
category.hide();
// etc...
category.show();
Expanding
This only applies to categories which contain other nested categories.
An expanded category will show you its sub categories. By default, nested categories are collapsed, and need to be clicked to be expanded.
JSON
{
"kind": "category",
"name": "...",
"expanded": "true"
}
XML
<category name="..." expanded="true"></sep>
Styling
Blockly provides a default categories UI, and with it some basic options for styling. If you want information about how to do more advanced styling/configuration of the UI check out the Customizing a Blockly toolbox codelab and the 2021 Toolbox APIs talk.
Themes
Themes allow you to specify all of the colours of your workspace at once, including the colours of our categories.
To use them, you have to associate your category with a particular category style:
JSON
{
"kind": "category",
"name": "Logic",
"categorystyle": "logic_category"
}
XML
<category name="Logic" categorystyle="logic_category"></category>
Colours
You can also specify the colour directly, but this is not recommended. The colour is a stringified number (0-360) specifying the hue. Note the British spelling.
JSON
{
"contents": [
{
"kind": "category",
"name": "Logic",
"colour": "210"
},
{
"kind": "category",
"name": "Loops",
"colour": "120"
}
]
}
XML
<xml id="toolbox" style="display: none">
<category name="Logic" colour="210">...</category>
<category name="Loops" colour="120">...</category>
<category name="Math" colour="230">...</category>
<category name="Colour" colour="20">...</category>
<category name="Variables" colour="330" custom="VARIABLE"></category>
<category name="Functions" colour="290" custom="PROCEDURE"></category>
</xml>
Note that we also support using localizable colour references.
Category CSS
If you want more powerful customization, Blockly also allows you to specify CSS classes for different elements of the default UI. You can then use CSS to style these.
The following elements types can have CSS classes applied to them:
- container - The class for the parent div for the category. Default
blocklyToolboxCategory
. - row - The class for the div containing the category label and icon. Default
blocklyTreeRow
. - icon - The class for the category icon. Default
blocklyTreeIcon
. - label - The class for the category label. Default
blocklyTreeLabel
. - selected - The class that is added to the category when it is selected. Default
blocklyTreeSelected
. - openicon - The class added to an icon when the category has nested categories
and is open. Default
blocklyTreeIconOpen
. - closedicon - The class added to an icon when the category has nested categories
and is closed. Default
blocklyTreeIconClosed
.
And here is how you specify the classes using either format:
JSON
Set the CSS class of a particular element type using the cssConfig property.
{
"kind": "category",
"name": "...",
"cssConfig": {
"container": "yourClassName"
}
}
XML
Set the CSS class of a particular element type by prepending 'css-' to it.
<category name="..." css-container="yourClassName"></category>
Accessing
There are two ways you can access a category programmatically. You can either access it by index (where 0 is the top category):
var category = toolbox.getToolboxItems()[0];
Or by ID:
var category = toolbox.getToolboxItemById('categoryId');
Where the ID is specified in the toolbox definition:
JSON
{
"kind": "category",
"name": "...",
"toolboxitemid": "categoryId"
}
XML
<category name="..." toolboxitemid="categoryId"></category>
Preset Blocks
The toolbox definition may contain blocks that have fields set to a default value, or have blocks that are already connected together.
Here are four blocks:
- A simple
logic_boolean
block with no preset values:
- A
math_number
block that has been modified to display the number 42 instead of the default of 0:
- A
controls_for
block that has threemath_number
blocks connected to it:
- A
math_arithmetic
block that has twomath_number
shadow blocks connected to it:
Here is a toolbox definition containing these four blocks:
JSON
As of the September 2021 release, you can specify the state of blocks wthout
using 'blockxml'
.
{
"kind": "flyoutToolbox",
"contents": [
{
"kind": "block",
"type": "logic_boolean"
},
{
"kind": "block",
"type": "math_number",
"fields": {
"NUM": 42
}
},
{
"kind": "block",
"type": "controls_for",
"inputs": {
"FROM": {
"block": {
"type": "math_number",
"fields": {
"NUM": 1
}
}
},
"TO": {
"block": {
"type": "math_number",
"fields": {
"NUM": 10
}
}
},
"BY": {
"block": {
"type": "math_number",
"fields": {
"NUM": 1
}
}
},
}
},
{
"kind": "block",
"type": "math_arithmetic",
"fields": {
"OP": "ADD"
},
"inputs": {
"A": {
"shadow": {
"type": "math_number",
"fields": {
"NUM": 1
}
}
},
"B": {
"shadow": {
"type": "math_number",
"fields": {
"NUM": 1
}
}
}
}
},
]
}
Old JSON
Before the September 2021 release, you had to use the 'blockxml'
property to
specify the state of blocks.
{
"kind": "flyoutToolbox",
"contents": [
{
"kind": "block",
"type": "logic_boolean"
},
{
"kind": "block",
"blockxml":
'<block type="math_number">' +
'<field name="NUM">42</field>' +
'</block>'
},
{
"kind": "block",
"blockxml":
'<block type="controls_for">' +
'<value name="FROM">' +
'<block type="math_number">' +
'<field name="NUM">1</field>' +
'</block>' +
'</value>' +
'<value name="TO">' +
'<block type="math_number">' +
'<field name="NUM">10</field>' +
'</block>' +
'</value>' +
'<value name="BY">' +
'<block type="math_number">' +
'<field name="NUM">1</field>' +
'</block>' +
'</value>' +
'</block>'
},
{
"kind": "block",
"blockxml":
'<block type="math_arithmetic">' +
'<field name="OP">ADD</field>' +
'<value name="A">' +
'<shadow type="math_number">' +
'<field name="NUM">1</field>' +
'</shadow>' +
'</value>' +
'<value name="B">' +
'<shadow type="math_number">' +
'<field name="NUM">1</field>' +
'</shadow>' +
'</value>' +
'</block>'
},
]
}
XML
<xml id="toolbox" style="display: none">
<block type="logic_boolean"></block>
<block type="math_number">
<field name="NUM">42</field>
</block>
<block type="controls_for">
<value name="FROM">
<block type="math_number">
<field name="NUM">1</field>
</block>
</value>
<value name="TO">
<block type="math_number">
<field name="NUM">10</field>
</block>
</value>
<value name="BY">
<block type="math_number">
<field name="NUM">1</field>
</block>
</value>
</block>
<block type="math_arithmetic">
<field name="OP">ADD</field>
<value name="A">
<shadow type="math_number">
<field name="NUM">1</field>
</shadow>
</value>
<value name="B">
<shadow type="math_number">
<field name="NUM">1</field>
</shadow>
</value>
</block>
</xml>
Writing these definitions out by hand can be... a bit of a pain. Instead you can load your blocks into a workspace, and then run the following code to get the definitions. These calls work because the toolbox uses the same format for blocks as the serialization system.
JSON
console.log(Blockly.serialization.workspaces.save(Blockly.getMainWorkspace()));
XML
console.log(Blockly.Xml.workspaceToDom(Blockly.getMainWorkspace()));
You can also remove the x
, y
and id
properties, because those are ignored
by the toolbox.
Shadow blocks
Shadow blocks are placeholder blocks that perform several functions:
- They indicate the default values for their parent block.
- They allow users to type values directly without needing to fetch a number or string block.
- Unlike a regular block, they get replaced if the user drops a block on top of them.
- They inform the user of the type of value expected.
Disabled blocks
Disabled blocks cannot be dragged from the toolbox. Blocks can be individually
disabled using the optional disabled
property.
JSON
{
"kind": "flyoutToolbox",
"contents": [
{
"kind": "block",
"type":"math_number"
},
{
"kind": "block",
"type": "math_arithmetic"
},
{
"kind": "block",
"type": "math_single",
"disabled": "true"
}
]
}
XML
<xml id="toolbox" style="display: none">
<block type="math_number"></block>
<block type="math_arithmetic"></block>
<block type="math_single" disabled="true"></block>
</xml>
You can also programmatically disable or enable a block by using setEnabled
.
Variable fields
Variable fields may need to be specified differently when they are in a toolbox vs when they are simply serialized.
In particular, when variable fields are normally serialized to JSON, they only contain the ID of the variable they represent, because the variable's name and type are serialized separately. However, toolboxes do not contain that information, so it needs to be included in the variable field directly.
{
"kind": "flyoutToolbox",
"content": [
{
"type": "controls_for",
"fields": {
"VAR": {
"name": "index",
"type": "Number"
}
}
}
]
}
Separators
Adding a separator between any two categories will create a line and extra space between the two categories.
You can change the class for the separator in your JSON or XML toolbox definition.
JSON
{
"kind": "sep",
"cssConfig": {
"container": "yourClassName"
}
}
XML
<sep css-container="yourClassName"></sep>
Adding a separator between any two blocks will create a gap between the blocks. By default every block is separated from its lower neighbour by 24 pixels. This separation may be changed using the 'gap' attribute, which will replace the default gap.
This allows you to create logical groups of blocks in the toolbox.
JSON
{
"kind": "flyoutToolbox",
"contents": [
{
"kind": "block",
"type":"math_number"
},
{
"kind": "sep",
"gap": "32"
},
{
"kind": "block",
"blockxml": "<block type='math_arithmetic'><field name='OP'>ADD</field></block>"
},
{
"kind": "sep",
"gap": "8"
},
{
"kind": "block",
"blockxml": "<block type='math_arithmetic'><field name='OP'>MINUS</field></block>"
}
]
}
XML
<xml id="toolbox" style="display: none">
<block type="math_number"></block>
<sep gap="32"></sep>
<block type="math_arithmetic">
<field name="OP">ADD</field>
</block>
<sep gap="8"></sep>
<block type="math_arithmetic">
<field name="OP">MINUS</field>
</block>
</xml>
Buttons and Labels
You can put a button or label anywhere you can put a block in the toolbox.
JSON
{
"kind": "flyoutToolbox",
"contents": [
{
"kind": "block",
"type":"logic_operation"
},
{
"kind": "label",
"text": "A label",
"web-class": "myLabelStyle"
},
{
"kind": "label",
"text": "Another label"
},
{
"kind": "block",
"type": "logic_negate"
},
{
"kind": "button",
"text": "A button",
"callbackKey": "myFirstButtonPressed"
},
{
"kind": "block",
"type": "logic_boolean"
}
]
}
XML
<xml id="toolbox" style="display: none">
<block type="logic_operation"></block>
<label text="A label" web-class="myLabelStyle"></label>
<label text="Another label"></label>
<block type="logic_negate"></block>
<button text="A button" callbackKey="myFirstButtonPressed"></button>
<block type="logic_boolean"></block>
</xml>
<style>
.myLabelStyle>.blocklyFlyoutLabelText {
font-style: italic;
fill: green;
}
</style>
You may specify a CSS class name to apply to your button or label. In the above example, the first label uses a custom style, while the second label uses the default style.
Buttons should have callback functions; labels should not. To set the callback for a given button click, use
yourWorkspace.registerButtonCallback(yourCallbackKey, yourFunction).
Your function should accept as an argument the button that was clicked. The "Create variable..." button in the variable category is a good example of a button with a callback.
Changing the Toolbox
The application may change the blocks available in the toolbox at any time with a single function call:
workspace.updateToolbox(newTree);
As was the case during initial configuration, newTree
may either be a tree of
nodes, a string representation, or a JSON object. The only restriction is that
the mode cannot be changed; that is if there were categories in the
initially-defined toolbox then the new toolbox must also have categories
(though the categories may change). Likewise, if the initially-defined toolbox
did not have any categories, then the new toolbox may not have any categories.
The contents of a single category can be updated by:
var category = workspace.getToolbox().getToolboxItems()[0];
category.updateFlyoutContents(flyoutContents);
Where flyoutContents can be a list of blocks defined using JSON, a tree of nodes, or a string representation.
Be aware that at this time updating the toolbox causes some minor UI resets:
- In a toolbox without categories, any fields changed by the user (such as a dropdown) will revert to the default.
Here is a live demo of a tree with categories and block groups.