Blockstudio can be activated on a site by navigating to the Blockstudio menu item in the WordPress admin dashboard. Alternatively, it is also possible to activate Blockstudio using PHP. This is done by adding the following code constant to your functions.php file: const BLOCKSTUDIO_LICENSE = "YOUR LICENSE KEY";
Beside static styles and scripts as files, Blockstudio also supports dynamic asset blocks via the [code field]({{ site.link }}/documentation/attributes/field-types#code). Depending on your use case, these can be scoped to the block.
To avoid conflicts with other blocks, you can use the %selector% variable inside the code field alongside [useBlockProps]({{ site.link }}/documentation/components/useblockprops) in your rendering template.
Let's imagine that we want to target the h1 tag from the example above in our code field. %selector% h1 { color: red; } Now, Blockstudio will do three things: - create a unique id for that block instance - replace %selector% with the unique id - add the same selector to the element marked with useBlockProps The final output will be something like this:
Always having to render the style tag manually can be cumbersome. To make this process easier, you can use the asset attribute inside the code field. { "blockstudio": { "attributes": [ { "type": "code", "id": "code", "label": "Custom CSS", "language": "css", "asset": true } ] } } This will automatically create style tags for code blocks marked as css and move them to the head of the document. For fields marked with javascript as the language, script tags will be created instead and placed at the bottom of the body.
When using code fields inside of [extensions]({{ site.link }}/documentation/extensions), the asset attribute is not necessary. Blockstudio will automatically render the code field content as an asset if the language is css or javascript.
{ "$schema": "https://app.blockstudio.dev/schema/extend", "name": "core/*", "blockstudio": { "extend": true, "attributes": [ { "id": "customCss", "type": "code", "label": "Custom css", "language": "css" } ] } } No additional configuration is needed. The above will show a code field for every core/* block in the sidebar and automatically render the content as an asset to the page.
Blockstudio is able to automatically minify all CSS and JS files in a block. Compiled files will be saved to the _dist folder of the block and enqueued when a block is used. When the source file is updated, Blockstudio checks if a compiled file with that timestamp exists, if not, it will be created. The minify library is used for this purpose.
When using SCSS, it is possible to import other SCSS files. Imports are relative to the file they are imported from. @import "modules.scss"; Additionally, custom import paths can be defined using the blockstudio/assets/process/scss/importPaths filter, so files can be imported from other directories without specifying any folder structure. Please note that the .scss extension needs to be present for imports to work properly. Block assets will be recompiled when any of the imported files change.
All scripts (inline and default) in Blockstudio load with type="module" applied to them by default. This means that it is possible to make use of the import syntax for your blocks' scripts without any additional setup. Let's imagine the following code inside a script.js: import { h, render } from "https://esm.sh/preact@10.15.1"; import htm from "https://esm.sh/htm@3.1.1"; import confetti from "https://esm.sh/canvas-confetti@1.6.0"; const html = htm.bind(h); const styles = `display: block; font-family: sans-serif; color: black; background: #f1f1f1; padding: 1rem; border-radius: 0.25rem; margin: 1rem; font-family: sans-serif; `; function App(props) { confetti(); console.log(confetti); return html`<h1 style="${styles}">Hello ${props.name}!</h1>`; } render( html`<${App} name="Hello from Preact!" />`, document.querySelector("#element") ); The example above would create a component using Preact , importing all necessary components and functions from an ESM compatible CDN, in this case esm.sh. While there is nothing wrong with this approach, it has the disadvantage of needing to request the necessary assets from an external site, risking broken scripts if the CDN is down. Blockstudio includes a handy way to download ES Modules to the block, eliminating the need for external ESM CDNs during production.
To download a module to your block directory, simply swap the CDN url. import { h, render } from "blockstudio/preact@10.15.1"; import htm from "blockstudio/htm@3.1.1"; import confetti from "blockstudio/canvas-confetti@1.6.0"; Blockstudio will automatically check changed .js files for imports and download them to the local block directory. The downloaded files will be placed in a modules folder in the block directory. The script file will be rewritten on save to accustom for the locally hosted module, in the example above, the generated scripts would have the following import: import { h, render } from "./modules/preact/10.15.1.js"; import htm from "./modules/htm/10.15.1.js"; import confetti from "./modules/canvas-confetti/1.6.0.js"; When inside the editor, the CDN url will still be used (Blockstudio is using esm.sh) when previewing and working on the block. There you go! The world of NPM is at your fingertips without the boring boilerplate of setting up bundlers and other tools. If you are concerned about performance due to not having a single JS bundle, this article is worth a read. Since the use of ES modules using the above syntax is completely opt-in, there is no need to activate this feature, it is enabled by default.
Normally, modules can be used from CDNs without the version number. In this case, the newest version will always be used. Since Blockstudio is not saving any information into the database, a version number is required when using modules.
Modules are scoped to their blocks. Even if you use the same module with the same version number across multiple blocks, Blockstudio will still download the requested module to the block. This is mainly because blocks should be self-contained units that can easily be shared across other installations or sites. On top of that, the same module will be requested twice if both blocks are present on a page. This is not so much of an issue if the block is loading a very specific module like a slider library. However, if you are creating multiple blocks that rely on the same framework as above (Preact), loading the same module multiple times can become a performance issue. This problem can be solved by using the script-inline.js instead of the script.js file. Blockstudio will rewrite each of the imports to point to the location of the first occurrence of the module if the name and version number are the same.
It is also possible to import CSS files using the same syntax as above. import "blockstudio/swiper@11.0.0/swiper.min.css"; The CSS file will be downloaded to the modules folder and automatically enqueued when the block is used. As long as the version is the same, only a single version of the CSS file will be enqueued, even if it exists in multiple blocks.
Blockstudio will automatically enqueue all files ending with .css, .scss and .js when your block is being used on a page. It is possible to define how assets are being enqueued by using one of the following file names. The * is a wildcard that can be replaced with any string of your choice. - *.(s)css: enqueues as a tag in the editor and on the frontend - *-inline.(s)css: enqueues the contents of the file in an inline
Inline styles and scripts have the big advantage that they are directly rendered as style or script tags inside the page. This can enhance loading times, since it saves extra requests that would have to be made otherwise. - .js files are inlined to the end of the body - .(s)css files are inlined to the end of the head - each file is only being inlined once
Scoped styles are also inlined, but are prefixed with an ID that is unique to each block. Use the bs_get_scoped_class function to add the class to your template.
">
Scope me!
Scope me too!
h1 { color: red; } The above will result in the following scoped style: <div class="bs-62df71e6cc9a"> <h1>Scope me!</h1> <p>Scope me too!</p> </div>
Besides block specific assets, it is also possible to enqueue global assets, which will be available on all pages, regardless if a block is present. Enqueuing a global asset is done by adding the global- prefix to the file name. Any of the suffixes (e.g. -inline) can be used in combination. Possible combinations are: - global-styles.(s)css - global-styles-inline.(s)css - global-styles-editor.(s)css - global-styles-scoped.(s)css - global-scripts.js - global-scripts-inline.js - global-scripts-editor.js - global-scripts-view.js
Block editor assets are enqueued only in the block editor. The block-editor- prefix is used to define block editor assets. - block-editor-styles.(s)css - block-editor-scripts.js
When a block templates returns nothing, Blockstudio will not enqueue any assets for that particular block. This method comes in handy to disable enqueueing when a certain condition is met.
The $attributes or $a variables only give you access to data registered in the blockstudio property. To access information about standard block data like alignment or typography, simply use the $block or $b variables.
If you want to set a default value for a property that is set by WordPress like align, you can do so by adding a default key to the property definition inside the attributes key. { "$schema": "https://app.blockstudio.dev/schema", "name": "blockstudio/block", "title": "Native Block", "icon": "star-filled", "description": "Native Block.", "supports": { "align": ["left", "center", "right"] }, "attributes": { "align": { "type": "string", "default": "center" } }, "blockstudio": true }
There are 8 different operators which can be used: - == - values are equal - != - values are not equal - includes - value is included in reference value - !includes - value is not included in reference value - empty - value is empty - !empty - value is not empty - < - value is smaller than reference value - > - value is bigger than reference value - <= - value is smaller than reference value or equal - >= - value is bigger than reference value or equal
By default, Blockstudio comes with 4 global conditions: post type, post ID, user role and user ID. { "$schema": "https://app.blockstudio.dev/schema", "name": "blockstudio/native", "title": "Native Block", "category": "text", "icon": "star-filled", "description": "Native Blockstudio block.", "supports": { "align": true }, "blockstudio": { "attributes": [ { "id": "message", "type": "text", "label": "My message", "conditions": [ [ { "type": "postId", "operator": "==", "value": "1386" }, { "type": "postType", "operator": "==", "value": "post" } ] ] } ] } } In the example above, the text attribute will only show in the editor if the post ID is 1386 and the post type is post. Please note that the camelCase convention is being used for the type keys. (postType, postId, userRole, userId) If you want to create or conditions instead, simply move the conditions into their own array: { "blockstudio": { "attributes": [ { "id": "message", "type": "text", "label": "My message", "conditions": [ [ { "type": "postId", "operator": "==", "value": "1386" }, { "type": "postType", "operator": "==", "value": "post" } ], [ { "type": "postType", "operator": "==", "value": "jobs" } ] ] } ] } } In the example above, the text attribute will only show in the editor if the post ID is 1386 and the post type is post or the post type is jobs.
It is also possible to set conditions that work between attributes. Instead of setting a type key, simply set the id of the attribute you want to check against. { "blockstudio": { "attributes": [ { "type": "toggle", "id": "copyButton", "label": "Copy Button" }, { "type": "text", "id": "copyButtonText", "label": "Copy Button Text", "default": "Copy", "conditions": [ [ { "id": "copyButton", "operator": "==", "value": true } ] ] } ] } } You can combine global conditions with block condition as you please.
By default, conditions for attributes inside repeaters depend on the attributes of the currently repeated element. { "blockstudio": { "attributes": [ { "type": "toggle", "id": "toggle", "label": "Toggle" }, { "type": "repeater", "id": "repeater", "label": "Repeater", "attributes": [ { "type": "toggle", "id": "toggle", "label": "Toggle" }, { "type": "text", "id": "text", "label": "Text", "conditions": [ [ { "id": "toggle", "operator": "==", "value": true } ] ] } ] } ] } } Since attribute IDs are scoped to the current repeater element, the text attribute inside the repeater will only show if the toggle inside the repeater is set to true. If you want to check against the toggle outside the repeater, you can apply "context": "main" to the condition, and it will show the text attribute if the toggle outside the repeater is set to true. { "blockstudio": { "attributes": [ { "type": "toggle", "id": "toggle", "label": "Toggle" }, { "type": "repeater", "id": "repeater", "label": "Repeater", "attributes": [ { "type": "toggle", "id": "toggle", "label": "Toggle" }, { "type": "text", "id": "text", "label": "Text", "conditions": [ [ { "id": "toggle", "operator": "==", "value": true, "context": "main" } ] ] } ] } ] } }
Attributes can be deactivated on a per-block basis by hovering over the left side of the UI in the sidebar and clicking it when the blue border appears. This comes in handy when an attribute should be disabled temporarily, while leaving the filled content intact. The UI of the attribute will be slightly translucent if it is deactivated. The same principle also works for single elements when using the files attribute type:
The first method filters the attributes in the editor. This is useful if you want to adjust the default value of an attribute or its conditions. The code above will set the default value of the lineNumbers attribute to true and will hide the attribute if the language attribute is not set to css. Keep in mind that this filter is only evaluated when inserting blocks in the editor.
The second method filters the attributes on the frontend. This is useful if you want to adjust the attributes before they are passed to the block template. Keep in mind that the above filter will override any values set in the editor.
Sometimes it might be necessary to use the value of attributes inside your CSS or JS files. Blockstudio provides two helper functions to render the value of attributes as data attributes or CSS variables. Let's imagine a block with the following attribute structure: "blockstudio": { "attributes": [ { "id": "message", "type": "text", "label": "My message" }, { "id": "color", "type": "color", "label": "My color" } ] }
The bs_render_attributes function will render the attributes as data attributes on the element.
Hello
The bs_render_attributes function accepts a second parameter to specify the attributes to render. If no attributes are specified, all attributes will be rendered.
>
{{ a.message }}
The above example will render the following HTML:
Hello
If you don't want to render the output, simply use the bs_attributes function.
Similarly, the bs_render_variables function will render the attributes as CSS variables on the element. This is useful if you want to use the attributes in a CSS file.
{{ a.message }}
Hello
Similarly, the bs_render_variables function accepts a second parameter to specify the attributes to render. If no attributes are specified, all attributes will be rendered.
{{ a.message }}
The above example will render the following HTML:
Hello
If you don't want to render the output, simply use the bs_variables function.
The bs_render_attributes and bs_render_variables functions will convert attribute IDs to kebab case. For example, the IDs myAttribute or my_attribute will be converted to my-attribute.
Instead of supplying different field types like Posts, Users or Terms, Blockstudio allows for a more modular way of populating data right from the block.json. This feature currently works for select, radio,checkbox, color and gradient field types. Data can be populated in three different ways: - Query return results from a post, user or term query (only select, radio, checkbox) - Fetch return results from an external source (only select, radio,checkbox) - Function return results from a custom function - Custom return results from a custom dataset
When choosing the query mode, Posts, Users or Terms can be populated. Getting started is easy, simply add the populate attribute to your block.json and define the type that should be queried for your block. { "$schema": "https://app.blockstudio.dev/schema", "name": "blockstudio/native", "title": "Native Block", "category": "text", "icon": "star-filled", "description": "Native Blockstudio block.", "supports": { "align": true }, "blockstudio": { "attributes": [ { "id": "posts", "type": "select", "label": "Posts", "populate": { "type": "query", "query": "posts" // or "users" or "terms" } } ] } } Following functions are used internally: - posts: get_posts - users: get_users - terms: get_terms
By default, Blockstudio will return following data. - posts: value: post object, label: post_title - users: value: user object, label: display_name - terms: value: term object, label: name The response can be customised with the returnFormat attribute. { "blockstudio": { "attributes": [ { "id": "posts", "type": "select", "label": "Posts", "populate": { "type": "query", "query": "posts", "arguments": { "post_type": "posts", "numberposts": "20", }, "returnFormat": { "value": "id" // only accepts "id" "label": "post_name" // accepts any value found in the respective object } } } ] } }
The query population type allows fetching data from the server by using the fetch option. { "blockstudio": { "attributes": [ { "id": "posts", "type": "select", "label": "Posts", "populate": { "type": "query", "query": "posts", "fetch": true, "arguments": { "post_type": "posts", "numberposts": "20", } } } ] } } Using the settings above, Blockstudio will automatically enable stylisedUi for the attribute and initially fetch the first 20 posts from the get_posts query. Upon typing in a value in the select field, the search value will be used as the s argument in the query, returning posts for the search term. This will also work for the users and terms query types. When fetching users, you might have to adjust the search_columns property in the arguments object to get appropriate results for your query. See the get_users documentation for more information.
Instead of relying on the built-in query functions, it is possible to use a custom function to populate options. This can be useful when you want to return data not covered by the built in query types. { "blockstudio": { "attributes": [ { "id": "postTypes", "type": "checkbox", "label": "Post types", "populate": { "type": "function", "function": "get_post_types" } } ] } }
Similar to the query type, custom arguments can be passed to the function using the arguments key. Internally, Blockstudio uses call_user_func_array, so all arguments have to be passed as an array. { "blockstudio": { "attributes": [ { "id": "postTypes", "type": "checkbox", "label": "Post types", "populate": { "type": "function", "function": "get_post_types", "arguments": [[], "objects"] } } ] } }
Blockstudio will always look for the keys set in the returnFormat object. If not available, it will look for the value and label key in the returned data. If those are not available either, it'll fallback to the first value in the returned array. { "blockstudio": { "attributes": [ { "id": "postTypes", "type": "checkbox", "label": "Post types", "populate": { "type": "function", "function": "get_post_types", "arguments": [[], "objects"], "returnFormat": { "value": "name", "label": "label" } } } ] } }
It is possible to combine options and the data from queries or functions. { "blockstudio": { "attributes": [ { "id": "users", "type": "radio", "label": "Users", "options": { { "value": "administrators", "label": "Administrators", }, { "value": "editors", "label": "Editors", } } "populate": { "type": "query", "query": "users", "position": "before" // this will add the populate options before the static options (defaults to "after") } } ] } }
Instead of relying on one of the built-in ways to populate options with data, it is possible to create custom datasets that can be easily reused within fields. Adding custom data is done using the blockstudio/blocks/populate filter. Then call the dataset inside the block.json file. { "blockstudio": { "attributes": [ { "id": "posts", "type": "select", "label": "Posts", "populate": { "type": "custom", "custom": "customData", } } ] } }
It is possible to fetch data from an external source using the fetch type. The search will be appended to the searchUrl argument. { "name": "blockstudio/fetch", "title": "Fetch", "description": "Fetch field", "blockstudio": { "attributes": [ { "id": "select", "type": "select", "label": "Select", "multiple": true, "populate": { "type": "fetch", "arguments": { "urlSearch": "https://fabrikat.io/streamline/wp-json/wp/v2/posts?search=" }, "returnFormat": { "value": "id", "label": "title.rendered" } } } ] } }
Of course, blocks without dynamic data are kinda boring! Attributes allow you to add data to your blocks and are comparable to custom fields that you know from solutions like ACF or Metabox. Custom attributes are registered using the blockstudio property inside the block.json file. { "$schema": "https://app.blockstudio.dev/schema", "name": "blockstudio/native", "title": "Native Block", "category": "text", "icon": "star-filled", "description": "Native Blockstudio block.", "supports": { "align": true }, "blockstudio": { "attributes": [ { "id": "message", "type": "text", "label": "My message" } ] } } And that's it! You've just registered your first custom attribute:
To create a new code snippet, simply create a new folder and place an init.php file in it. Blockstudio will always execute that file. For more information on the init file, check the [Initialization]({{ site.link }}/documentation/initialization/) page.
Folders marked as code snippets will also process and enqueue styles and scripts. To enqueue global styles and scripts, use the global [prefix for the asset name]({{ site.link }}/documentation/assets/registering/#global). Assets inside code snippet folders also support all [processing]({{ site.link }}/documentation/assets/processing/).
The above technique can also be used on directories outside of the folder where all blocks are stored. For example, let's say you want to store global assets like styles and scripts inside your theme. Simply create a new Blockstudio instance using the [init method]({{ site.link }}/documentation/registration/#instances). add_action('init', function () { Blockstudio\\Build::init([ 'dir' => get_template_directory() . '/assets' ]); }); Now, simply follow the setup instructions inside the to register a code snippet inside the assets folder.
Inner blocks allow you to insert additional blocks to your blocks. Under the hood, Blockstudio is using the InnerBlocks component. Please note that it is only possible to use one InnerBlocks component per block, this is a WordPress limitation.
To use InnerBlocks, you need to add the InnerBlocks component to your block. or The composition is up to you. You can nest the InnerBlocks component as deep as you want or just use it by itself.
The templateLock prop allows you to define if the template can be modified or not. - contentOnly: prevents all operations. Additionally, the block types that don't have content are hidden from the list view and can't gain focus within the block list. Unlike the other lock types, this is not overrideable by children. - all: prevents all operations. It is not possible to insert new blocks. Move existing blocks or delete them. - insert: prevents inserting or removing blocks, but allows moving existing ones. - false: prevents locking from being applied to an InnerBlocks area even if a parent block contains locking.
The renderAppender prop allows you to define the block appender type. - default: display the default block appender, typically the paragraph style appender when the paragraph block is allowed. - button: display a + icon button as the appender.
Blockstudio supports context for the InnerBlocks component. This allows you to pass data from the parent block to the child blocks. While it uses the WordPress core context mechanism under the hood, Blockstudio provides a more convenient API. Instead of having to define all attributes that should be passed in the context separately, you can simply subscribe to all attributes of a parent block. Let's say you have a container block setup like so: { "$schema": "https://app.blockstudio.dev/schema", "name": "blockstudio/container", "title": "Container", "category": "design", "icon": "star-filled", "description": "Container block.", "blockstudio": { "attributes": [ { "id": "full-width", "type": "toggle", "label": "Makes section full width" } ] } } Inside your child block, simply use the usesContext property with the parent block name to gain access to all parent attributes in your block template. { "$schema": "https://app.blockstudio.dev/schema", "name": "blockstudio/element", "title": "Element", "category": "design", "icon": "star-filled", "description": "Element block.", "usesContext": ["blockstudio/container"], "parent": ["blockstudio/container"], "blockstudio": true }
The $context and $c variables are available inside your block templates to access all parent attributes. Child blocks will automatically reload in the editor if parent data changes.
{{ container['full-width'] ? 'is full width' : 'no full width' }}
InnerBlocks has to render a wrapper element around its children inside the editor, however, it can be removed from the frontend by using blockstudio/blocks/components/innerblocks/frontend/wrap filter.
If you want to return early from the render method of your block, you can check if the $innerBlocks (it contains the content) variable is empty. This comes in handy if you don't want to render anything if there are no inner blocks. Since is will always render an empty paragraph tag, you can use the strip_tags function to check if the content is empty.
The MediaPlaceholder component provides a placeholder to add media items to a block. The same component is being used in the core/image block. It is based on the React component with the same name.
To use MediaPlaceholder in its most basic form, you need to add the appropriate attribute to your block.json and add the MediaPlaceholder component to your block. { "$schema": "https://app.blockstudio.dev/schema", "name": "blockstudio/media-placeholder", "title": "Native Block", "category": "text", "icon": "star-filled", "description": "Native Blockstudio MediaPlaceholder block.", "blockstudio": { "attributes": [ { "id": "media", "type": "files", } ] } } or or {{ fn('wp_get_attachment_image', file.ID) }} Once a media file has been selected, the MediaPlaceholder component will not be rendered anymore.
A string passed to FormFileUpload that tells the browser which file types can be upload to the upload window the browser use e.g.: image/* or video/*. More information about this string is available in here.
Array with the types of the media to upload/select from the media library. Each type is a string that can contain the general mime type e.g.: image, audio, text, or the complete mime type e.g.: audio/mpeg, image/gif. If allowedTypes is unset, all mime types should be allowed.
If true, the Drop Zone will not be rendered. Users won't be able to drag & drop any media into the component or the block. The UI controls to upload the media via file, url or the media library would be intact.
If true, only the Drop Zone will be rendered. No UI controls to upload the media will be shown. The disableDropZone prop still takes precedence over dropZoneUIOnly – specifying both as true will result in nothing to be rendered.
If true, the property changes the look of the placeholder to be adequate to scenarios where new files are added to an already existing set of files, e.g., adding files to a gallery. If false, the default placeholder style is used.
The RichText component allows rendering an editable input inside the editor and a static HTML Element in the frontend. It is based on the RichText component WordPress provides.
To use RichText in its most basic form, you need to add the appropriate attribute to your block.json and add the RichText component to your block. { "$schema": "https://app.blockstudio.dev/schema", "name": "blockstudio/rich-text", "title": "Native Block", "category": "text", "icon": "star-filled", "description": "Native Blockstudio RichText block.", "blockstudio": { "attributes": [ { "id": "myRichText", "type": "richtext", } ] } } or You can use an unlimited number of RichText components in your block.
Whether to preserve white space characters in the value. Normally tab, newline and space characters are collapsed to a single space. If turned on, soft line breaks will be saved as newline characters, not as line break elements.
By default, Gutenberg has to create a wrapper around blocks to make them interactive inside the editor. This can become problematic for some block types like containers or columns, since the markup between editor and frontend will be different. The useBlockProps hook makes it possible to mark an element inside the block template as the root element, thus creating markup parity between editor and frontend.
Simply add the useBlockProps attribute to the root element of your block.
Blockstudio will automatically combine classes and attributes from the editor (alignment classes etc.) along with whatever is defined in the block templates. get_block_wrapper_attributes is being used under the hood for that.
Since there can only be one root element in a block template, when using useBlockProps, the block template has to have a single root element. For example, the following will not work and Gutenberg will create a wrapper around the template.
First root element.
Second root element. Similarly, useBlockProps can only be used once per block template.
Blockstudio converts the block template to valid React code inside Gutenberg using html-react-parser. This comes with the limitation that the @ character can't be used inside an HTML attribute name. Libraries like Alpine.js use the @ characters to define directives on HTML elements. Inside Gutenberg, this @click attribute will be stripped from the element. (it will still work on the frontend, of course) To combat this issue, you can use the alternative syntax Alpine.js provides. The above will work inside Gutenberg and on the frontend.
Blockstudio can be added to projects using composer with the private-composer-installer package. It allows you to install packages from private URLs within composer.json.
It is good practice to store all your credentials and private keys in an .env file. If you haven't done so already, create a .env file in the root of your project and add your Blockstudio license key BLOCKSTUDIO_KEY=your-blockstudio-license-key
When blocks are used inside a loop (for example, a Query block), the block is executed once for each iteration of the loop. Blockstudio provides handy shortcuts for accessing the current loop and outer context.
This is the data of current element inside the loop:
This is the data the current post:
This is the data of current element inside the loop: {{ block.context.postId }} {{ block.context.postType }}
This is the data the current post: {{ block.postId }} {{ block.postType }}
The default value for attributes will be used in the editor. If you prefer to preview the block with different data, you can use the example key. Keep in mind that the example data will be also be used when [previewing]({{ site.link }}/documentation/environment/#preview) the block
Blockstudio includes a full code editor, capable of editing and creating blocks all within the WordPress admin area. The current feature set includes: - Creating a custom plugin if no block source has been found - Creating new files - Editing files - Renaming files - Deleting files - Creating new blocks - Deleting blocks - Importing data - Exporting data - Editing assets - Adding assets - Deleting assets - Live preview of blocks
Just like many other popular online IDEs, Blockstudio is using the Monaco Editor for code editing. Since VS Code is powered by this very same editor, VSC users should feel right at home with it. Besides that, the editor interface is making heavy use of Gutenberg components, ensuring similarities in look and behaviour to the WordPress admin area and Gutenberg itself.
Although there are checks in place that will prevent the saving of blocks in case the code creates a critical error on your site, it is advisable to either use the editor locally or on a staging environment. Keep in mind that the Blockstudio editor is not much different from using an editor on your computer, as it directly modifies the actual files in your block directory.
This will activate the editor (accessible in the WordPress admin menu) for the users with the ID of 1 and 19. This will activate the editor for all users with the role of administrator and editor.
Assets that were registered using wp_enqueue_scripts, admin_enqueue_scripts or enqueue_block_editor_assets can be added. Similarly, it is also possible to add additional styles for just a single block using the following configuration inside the block.json: { "name": "blockstudio/native", "title": "Native Block", "category": "text", "icon": "star-filled", "description": "Native Blockstudio block.", "blockstudio": { "editor": { "assets": ["stylesheet-handle"] } } }
The introduction of the editor is an exciting development for Blockstudio users and WordPress developers working with Gutenberg. Now that there is an accompanying interface for block directories, importing custom blocks from the library directly into your theme will be possible in the near future. On top of that, more work will be put into removing the barriers for integrated block development within WordPress. Tailwind, SCSS and a neat solution for web components as blocks are on the cards, so stay tuned!
Since 5.2, it is possible to edit blocks directly from Gutenberg. By clicking the "Edit with Blockstudio" button in the sidebar, the editor will open in a separate window and allow you to make changes to the block itself or its assets and preview them inside Gutenberg. Editing blocks using this method has one advantage over the built-in editor preview that Blockstudio provides: the same block inserted multiple times with different settings can be previewed at once. Please note that the editor needs to be active in order for the preview button to show up.
Blockstudio uses the Tailwind CDN version to compile your Tailwind classes when developing in the editor. Upon saving a block, all used Tailwind classes are saved to a file and automatically enqueued on the frontend and block editor.
If you need to recompile Tailwind classes after changes in your templates not made with the Blockstudio editor, you can do so by simply saving the settings again. This will recompile the Tailwind classes and save them to the file again.
Sometimes it is necessary to show content in your blocks exclusively when it is being rendered inside the Gutenberg editor and not on the frontend. This technique is very useful to provide users with helpful information and placeholders if the necessary data hasn't been input yet.
This content is only going to be rendered inside the editor. This content is only going to be rendered on the frontend. This content is only going to be rendered inside the editor. This content is only going to be rendered on the frontend.
Blockstudio adds another environment variable that will come in handy for block developers. $isPreview allows you to conditionally render content inside the block preview window when hovering over a block inside the block inserter. This content is only going to be rendered inside the block preview. This content is only going to be rendered on the frontend and editor. This content is only going to be rendered inside the block preview. This content is only going to be rendered on the frontend and editor.
Extensions allow you to extend any registered block in your WordPress installation using a simple .json based API, which is similar to the block.json configuration. This includes core, third-party and blocks made with Blockstudio. All [field types]({{ site.url }}/documentation/attributes/field-types) can be added to any block, with the data being saved to the block's attributes. The complete feature set of attributes like [filtering]({{ site.url }}/documentation/attributes/filtering), [disabling]({{ site.url }}/documentation/attributes/disabling), [conditional logic]({{ site.url }}/documentation/attributes/conditional-logic) and [populating options]({{ site.url }}/documentation/attributes/populating-options) are available for extensions.
Extended values can now be utilized in more versatile ways: - class: Add a class using the attribute value to the block. - style: Add inline styles using the attribute value to the block. - attributes: Set any attribute value directly on the block, enhancing its functionality or accessibility. - data attributes: Use extension values to set specific data attributes, enabling more complex interactions and dynamic content adjustments.
Imagine the following scenario: you want to add a custom class select field to every core block. All you need to do is add this .json file anywhere in your block folder, and it will be automatically registered as an extension. { "$schema": "https://app.blockstudio.dev/schema/extend", "name": "core/*", "priority": 10, "blockstudio": { "extend": true, "attributes": [ { "id": "customColor", "type": "select", "label": "Custom color", "options": [ { "value": "#f00", "label": "Red" }, { "value": "#00f", "label": "Blue" } ], "set": [ { "attribute": "style", "value": "color: {attributes.customColor}" } ] } ] } } This will add a customColor field to every core block, allowing you to set the color of the block's text. Let's break down the configuration from top to bottom:
Since there is no rendering template, an extension doesn't need a matching index file and thus doesn't need to be named block.json. This allows storing all extensions conveniently in a single folder.
The name property of the configuration object defines which blocks should be extended. There are three options depending on your needs: - blockName: A single block name, e.g. core/paragraph. - [blockNameA, blockNameB]: An array of block names, e.g. ["core/paragraph", "core/heading"]. - core/*: A wildcard to extend all blocks of a certain type, e.g. core/*.
By default, extensions will render in its own tab in the block inspector. If you want to move the extensions to a different position, you can use the group property. The following values are currently available: - settings: The default position. - styles: The styles tab. - advanced: The advanced tab. { "$schema": "https://app.blockstudio.dev/schema/extend", "name": "core/*", "blockstudio": { "extend": true, "group": "styles", "attributes": [] } }
The priority property allows you to define the order in which extensions are rendered. Extensions with a higher priority will be rendered later. This is useful if you want an extension to appear before another one.
For more information on how attributes work, please refer to the [attributes section]({{ site.url }}/documentation/attributes/field-types). The only thing you need to know about attributes inside extensions is the set property. It is used to apply the value of the attribute to one of the [types]({{ site.url }}/documentation/extensions#extension-types) of the block. For example, the following configuration will set the style attribute of the block to color: red or color: blue depending on the value of the select field. { "$schema": "https://app.blockstudio.dev/schema/extend", "name": "core/*", "blockstudio": { "extend": true, "attributes": [ { "id": "customColor", "type": "select", "label": "Heading color", "options": [ { "value": "#f00", "label": "Red" }, { "value": "#00f", "label": "Blue" } ], "set": [ { "attribute": "style", "value": "color: {attributes.customColor}" } ] } ] } }
Dot notation inside curly braces is used to get the value of an attribute. It is also possible to get nested values when attributes are not returning a single value, for example, when setting the return format to both on field types with options. { "attributes": [ { "id": "customColor", "type": "select", "label": "Heading color", "options": [ { "value": "#f00", "label": "Red" }, { "value": "#00f", "label": "Blue" } ], "returnFormat": "both", "set": [ { "attribute": "style", "value": "color: {attributes.customColor.value}" }, { "attribute": "data-color", "value": "{attributes.customColor.label}" } ] } ] } The example above will set the style attribute to color: #f00 or color:#00f and the data-color attribute to Red or Blue depending on the value. For now, only values from the current attribute can be used inside set definitions. The ability to use values from other attributes might be added in the future.
Blockstudio enables you to create custom WordPress blocks with nothing but PHP using the WordPress block.json format. It greatly simplifies the developer experience in regard to block registration, lazy loading assets, and custom fields.
There is no secret sauce. Blockstudio is built on top of core WordPress features and Gutenberg components. It is designed to be a lightweight abstraction layer that helps you get started with custom blocks quickly.
When creating dynamic blocks using nothing but WordPress, there is duplication of logic between PHP and JS code. There are discussions about solving this issue, including hydrating blocks on the client side (not good for SEO) or introducing a JSX parser for PHP (complex). Blockstudio takes a different approach. You use PHP to write your block templates and sprinkle in interactivity like or using JSX-like tags. Inside the editor, those tags will be replaced with their React counterparts. On the frontend, the tags will be replaced with HTML content. This way, you get the best of both worlds: a server-side rendered block with great interactivity in the editor.
While WordPress supports a file-based approach using the WPDefinedPath string type, it is also possible to link assets or templates from other destinations. Blockstudio doubles down on the file-system as the primary (and only) way of registering blocks. This decision was not made to restrict, but to set a block structure in place that can't be argued with. For example, if a style.css or script.js file is found in the same folder as a block.json, then you can be sure that it belongs to this exact block only.
Blockstudio just works. It is designed to remove friction when composing custom blocks and doesn't come in your way while doing so. Want to use Twig for your template? Simply rename the file extension. Need to include some CSS? Create a style.css file. Blockstudio will automatically enqueue it whenever the block is being used on your page, even when it is used outside the post_content with the [rendering function]({{ site.url }}/documentation/rendering-blocks). Do you have feature requests or questions? Message us.
Besides PHP hooks, Blockstudio also provides JavaScript events to manipulate values and blocks inside the editor. Since events are tied to block names and attribute IDs, we will use namespace/my-block as the name in the following examples. Of course, you will need to replace it with your own block name.
This event is dispatched when the blocks are loaded. It can be used to initialise code once the block has loaded in the editor. Alternatively, you can listen to all blocks.
This event is dispatched when the blocks are rendered. It can be used to initialise code once the block has rendered in the editor. Alternatively, you can listen to all blocks.
This event is dispatched when an attribute is updated. For this example we will imagine a text field with the ID of text. Alternatively, you can listen to all attribute updates.
Since IDs are not necessarily unique across attributes (for example inside and outside repeaters), you can specify a key inside the attribute object to uniquely identify the attribute when using events. { "blockstudio": { "attributes": [ { "id": "repeater", "type": "repeater", "label": "Repeater", "attributes": [ { "id": "text", "type": "text", "label": "Text" }, { "id": "repeater", "type": "repeater", "label": "Repeater", "textButton": "Add repeater", "attributes": [ { "key": "my-unique-key", "id": "text", "type": "text", "label": "Text" } ] } ] } ] } } With the setup above, listening to the key instead of the ID will only trigger the event when the text field inside the repeater is updated. document.addEventListener( "blockstudio/namespace/my-block/attributes/my-unique-key/update", function (event) { const { attribute, attributes, block, value, clientId, repeaterId } = event.detail; } );
This filter allows you to adjust the path of the block folder. By default, this is blockstudio inside the currently active theme. Alternatively it is possible to [create a new instance]({{ site.link }}/documentation/registration/#instances) for multiple source directories.
This filter allows you to adjust the data of the block.json file before it is registered. The above code would change the icon of all blocks starting with marketing.
This filter allows you to adjust the attributes of a block before the block is registered. See [Filtering]({{ site.link }}/documentation/attributes/filtering/) for more information.
This filter allows you to adjust the attributes of a block before it is rendered. See [Filtering]({{ site.link }}/documentation/attributes/filtering/) for more information.
This filter allows you to add custom data to the options of a checkbox, select or radio field. See [Populating options]({{ site.link }}/documentation/attributes/populating-options/) for more information.
Block templates will only be executed when the block is rendered. This is enough for most blocks; however, sometimes you need to execute code during an earlier stage of execution. For example, you may want to register a new post type or do some other type of setup unrelated to the block. To do this, you can add a PHP file that starts with init-, like init.php or init-post-types.php to your block directory. This file is executed during the init action. For more information on this specific stage, see the WordPress documentation. Any init.php file that is found within the block directory will be executed, regardless if it is part of a block context or not. This makes it perfect for organizing code snippets that are not related to any certain blocks.
Since version 3.1.0, Blockstudio includes a library of prebuilt elements. Of course, these elements are making use of all the features that are available to Blockstudio users. In fact, the library is actively being used to dogfood our own product, helping us fast-track development and ensure a good developer experience for everyone. The library is a first step in making Blockstudio more available as a general collection of ready-to-use blocks for non developers, all the while being built with the most advanced block framework for WordPress. More blocks are actively being developed!
Blockstudio blocks are dynamic, meaning that they must be rendered on the server to be displayed in Gutenberg. This rendering is facilitated by a fetch call to the Blockstudio block renderer. Upon first opening the editor, each block fetches its respective template, which results in multiple requests depending on the number of blocks present on the page. On some hosts, this can cause timeouts and errors while loading other essential assets needed for the editor to function properly.
Rendering template loading can be disabled by setting the disableLoading key in block.json to true. A placeholder will be rendered in place of the block template. All field settings will still be available when opening the sidebar. { "name": "blockstudio/native", "title": "Native Block", "category": "text", "icon": "star-filled", "description": "Native Blockstudio block.", "blockstudio": { "blockEditor": { "disableLoading": true } } } }
Blockstudio includes the possibility to override blocks on a very granular level. This can be useful for shared block libraries when you want to change the behavior of a block without having to create a new block from scratch.
To get started, set the name of the block you want to override as the name key and set the override key to true. { "name": "blockstudio/my-block", "title": "My overridden block title", "blockstudio": { "override": true } } When the override property is set to true, the data from the new block.json will be merged with the data from the original block. This means that you can override any property of the original block.
Attributes can either be overridden by id or added additionally. For example, let's imagine that we want to override the width attribute of a block and add a new height attribute. { "name": "blockstudio/my-block", "title": "My overridden block title", "blockstudio": { "override": true, "attributes": [ { "id": "width", "type": "number", "default": 280, "label": "Override width" }, { "id": "height", "type": "number", "default": 280, "label": "New height" } ] } } When an attribute with the same id is found, the data will be merged with the original attribute. Attributes which are not found will be added to the block. Important: When overriding attributes, you have to provide the type of the original attribute, even if it stays the same.
Asset files will be replaced when the file name matches. For example, if a style.css file exists in the original block, it will be replaced by the style.css file in the overriding block. Files with names that do not match the original block will be added to the block.
Twig templates can be overridden on a more granular level using the block feature in Twig. Let's imagine the following rendering template for a fictional image block:
Images:
Instead of replacing the whole template, we can use Twigs extends feature to override certain parts of the template while keeping the rest intact. {% block image %} <figure> <img src="{{ image.url }}" class="image" /> <figcaption>{{ image.caption }}</figcaption> </figure> {% endblock %}
Since block templates are just normal PHP files, you can use any PHP code you want in them. However, since blocks don't have access to the global $post variable inside the editor, get_the_ID() and other functions that depend on the variable will only work on the frontend. Blockstudio provides four different ways to access the current post ID in the editor and the frontend. echo $post_id; echo $postId; echo $block['postId']; echo $b['postId']; {{ post_id }}; {{ postId }}; {{ block.postId }}; {{ b.postId }};
By default, blocks won't be refreshed when you save or update a post. Thus, old data might be displayed in the editor after saving. To fix this, you can use the refreshOn key inside your block.json to tell Blockstudio to refresh the block when a post is saved. { "name": "blockstudio/native", "title": "Native Block", "category": "text", "icon": "star-filled", "description": "Native Blockstudio block.", "blockstudio": { "refreshOn": [ "save" ] } }
Gutenberg allows previewing blocks when hovering over the block in the fixed block inserter. To enable the preview of blocks made with Blockstudio, simply set the example key to an empty object. { "name": "blockstudio/native", "title": "Native Block", "category": "text", "icon": "star-filled", "description": "Native Blockstudio block.", "example": {}, "blockstudio": true }
It is also possible to provide structured data in the example key, so any custom attributes are correctly being rendered. { "name": "blockstudio/native", "title": "Native Block", "category": "text", "icon": "star-filled", "description": "Native Blockstudio block.", "example": { "attributes": { "title": "This title will be shown in the preview" } }, "blockstudio": true }
If your block relies on InnerBlocks, it is possible to provide a template for the InnerBlocks. { "name": "blockstudio/native", "title": "Native Block", "category": "text", "icon": "star-filled", "description": "Native Blockstudio block.", "example": { "innerBlocks": [ { "name": "core/paragraph", "attributes": { "content": "This is the default InnerBlocks block." } } ] }, "blockstudio": true }
Composing your own blocks with Blockstudio is extremely easy. The plugin will look for a blockstudio folder within your currently activated theme. Inside it, all subfolders that contain a block.json file with a blockstudio key will be registered.
So far, the custom block is not going to be registered since it is missing a template. To fix that just create an index.php or index.twig in the same folder as your block.json file and its contents will automatically be rendered when your block is used.
Twig is a flexible, fast, and secure template engine for PHP. It allows developers to write concise and readable code in templates, enhancing productivity and maintainability. Twig supports a variety of features designed to make templating both powerful and straightforward, making it ideal for projects that require robust, reusable templates.
Blocks can be registered conditionally using the conditions key. It supports all the global variables which are also available for attributes. See all conditions The following example will only register the block if the current post type is a page: { "blockstudio": { "conditions": [ [ { "type": "postType", "operator": "==", "value": "page" } ] ], } }
Custom SVGs icons for blocks can be registered like so: { "blockstudio": { "icon": "" } } WordPress doesn't allow for custom SVG icons inside its own block.json icon key, only Dashicons IDs are allowed here.
Blockstudio is using a React element parser to render the icon element, so it is possible to use HTML inside the icon string. { "blockstudio": { "icon": "
By default, Blockstudio will recursively look through the blockstudio folder inside your current theme or child theme for blocks. You can change that behaviour in two ways.
// Custom path within your theme. add_filter('blockstudio/path', function () { return get_template_directory() . '/blocks'; }); // Custom path within a plugin. add_filter('blockstudio/path', function () { return plugins_url() . '/my-custom-plugin/blocks'; });
If the above options are not enough, it is possible to initiate the Blockstudio\\Build class on various folders of your choice: add_action('init', function () { Blockstudio\\Build::init([ 'dir' => get_template_directory() . '/client-blocks' ]); });
Metadata can be filtered before the block is being registered using the blockstudio/block/meta filter: The example above is being used internally to give all Blockstudio library elements the same icon.
With Gutenberg becoming the prominent instrument in creating easily editable websites for clients, it makes sense to create all necessary website areas as blocks. While this approach will cater to most, advanced users and specific use cases might need to use those existing blocks outside the editor. Blockstudio provides two specific functions for exactly this use case: - bs_render_block: immediately renders the block to the page (no "echo "needed) - bs_block: needs an "echo" to be rendered to the page. This function is useful when nesting blocks. All Blockstudio specific features like inline styles, scripts, and scoped styles are supported when using the render functions.
In its simplest form, the function accepts a single value which is the ID of the block that should be rendered on the page. bs_render_block('blockstudio/cta');
To render the block with custom data, an array needs to be used in place of a single value for the first parameter. The value in the data key will be passed to the $attributes and $a variable inside your block template. bs_render_block([ 'id' => 'blockstudio/cta', 'data' => [ 'title' => 'My title', 'subtitle' => 'My subtitle', ], ]);
It is also possible to create multiple content slots by simply making the $content variable an associative array and calling it approriate keys in the bs_block function.
To improve development with autocomplete and validation in your IDE, Blockstudio introduces its own schema: View Blockstudio schema The Blockstudio schema is an (always up-to-date) copy of the WordPress core block.json schema with an additional blockstudio property that houses all plugin features like field schemas without interfering with potential future core properties
Simply add a $schema key with a value of https://app.blockstudio.dev/schema to your block.json and your IDE should start to give you hints about Blockstudio specific properties. { "$schema": "https://app.blockstudio.dev/schema", "name": "blockstudio/native", "title": "Native Block", "category": "text", "icon": "star-filled", "description": "Native Blockstudio block.", "blockstudio": true }
Blockstudio includes a powerful settings API, that allows setting options via a blockstudio.json file inside your theme folder and/or filters. Additionally, allowed users are able to change the settings visually inside the admin area.
If a blockstudio.json file is present inside your theme folder, it will be used to set the default options for the current site. A JSON schema is available to validate the file and help with autocompletion when used in an IDE. The following properties are available. { "$schema": "https://app.blockstudio.dev/schema/blockstudio", "users": { "ids": [], "roles": [] }, "assets": { "enqueue": true, "minify": { "css": false, "js": false }, "process": { "scss": false } }, "editor": { "formatOnSave": false, "assets": [], "markup": false }, "library": false }
Alternatively you can use the blockstudio/settings/${setting} filter to set options via PHP for more flexibility. add_filter('blockstudio/settings/assets/enqueue', '__return_false'); add_filter('blockstudio/settings/editor/formatOnSave', '__return_true'); add_filter('blockstudio/settings/library', '__return_true'); Options set via the blockstudio/settings/${setting} filter will override the ones set via the blockstudio.json file. Both methods can be used together.
Allowed users from the blockstudio/settings/users/ids and blockstudio/settings/users/roles filters are able to change the settings visually inside the admin area. If the Save as JSON checkbox is checked, the settings will be saved to the JSON file, otherwise they will be saved to the database into the options table. Settings set via filters will be grayed out and disabled inside the admin area.
The Block Transforms API allows you to define ways in how to transform blocks. Blockstudio currently supports three different types: - block to block: transform a block into another block - enter: insert a block when the user enters text and presses Enter - prefix: insert a block when the user enters text and presses Space
Block to block transforms allow you to transform a block into another block. { "$schema": "https://app.blockstudio.dev/schema", "name": "blockstudio/transforms", "title": "Native Transforms", "icon": "star-filled", "description": "Native Blockstudio Transforms.", "blockstudio": { "transforms": { "from": [ { "type": "block", "blocks": [ "blockstudio/transforms-2", "blockstudio/transforms-3" ] } ] }, "attributes": [ { "id": "text", "type": "text", "label": "Text" } ] } } When transforming into the new block, the attributes of the old block will be passed to the new block. In the example above, the text attribute will be passed to the new block. The same goes for all InnerBlocks content. It is important to note that the attribute types have to be the same for the block being transformed into and the block being transformed from. For example, if the block being transformed into has an attribute with the ID of text but the type of number, it won't appear in the transform list, as this would cause a type mismatch and rendering error. The number of attributes between the two blocks doesn't have to be the same.
Enter transforms allow you to insert a block when the user types certain content and then presses the Enter key. { "blockstudio": { "transforms": { "from": [ { "type": "enter", "regExp": "/^-{3,}$/" } ] } } } In the example above, the block will be inserted when the user types three or more dashes (-) and then presses Enter.
Prefix transforms allow you to insert a block when the user types certain content and then presses the Space key. { "blockstudio": { "transforms": { "from": [ { "type": "prefix", "regExp": "???" } ] } } } In the example above, the block will be inserted when the user types three question marks (?) and then presses Space.
The Block Variation API allows you to register custom variations for existing blocks. Let's imagine a block that should have two different variations depending on the value of a simple select field. { "$schema": "https://app.blockstudio.dev/schema", "name": "blockstudio/variations", "title": "Native Variation", "description": "Native Blockstudio Variation block.", "icon": "star-filled", "variations": [ { "name": "variation-2", "title": "Native Variation 2", "description": "Native Blockstudio Variation block 2.", "attributes": { "select": { "value": "variation-2" } } } ], "blockstudio": { "attributes": [ { "id": "select", "type": "select", "label": "Select", "options": [ { "value": "variation-1", "label": "Variation 1" }, { "value": "variation-2", "label": "Variation 2" } ], "default": { "value": "variation-1" } } ] } } The above will create three blocks in the inserter, the main one, and two variations.
Attributes will automatically render the appropriate input in the sidebar. Since variation blocks depend on specific attribute values, you might want to hide those fields from the sidebar. To do so, you can use the hidden option. { "blockstudio": { "attributes": [ { "id": "select", "type": "select", "label": "Select", "hidden": true, "options": [ { "value": "variation-1", "label": "Variation 1" }, { "value": "variation-2", "label": "Variation 2" } ] } ] } }
Blockstudio can be activated on a site by navigating to the Blockstudio menu item in the WordPress admin dashboard. Alternatively, it is also possible to activate Blockstudio using PHP. This is done by adding the following code constant to your functions.php file: const BLOCKSTUDIO_LICENSE = "YOUR LICENSE KEY";
Beside static styles and scripts as files, Blockstudio also supports dynamic asset blocks via the [code field]({{ site.link }}/documentation/attributes/field-types#code). Depending on your use case, these can be scoped to the block.
To avoid conflicts with other blocks, you can use the %selector% variable inside the code field alongside [useBlockProps]({{ site.link }}/documentation/components/useblockprops) in your rendering template.
Let's imagine that we want to target the h1 tag from the example above in our code field. %selector% h1 { color: red; } Now, Blockstudio will do three things: - create a unique id for that block instance - replace %selector% with the unique id - add the same selector to the element marked with useBlockProps The final output will be something like this:
Always having to render the style tag manually can be cumbersome. To make this process easier, you can use the asset attribute inside the code field. { "blockstudio": { "attributes": [ { "type": "code", "id": "code", "label": "Custom CSS", "language": "css", "asset": true } ] } } This will automatically create style tags for code blocks marked as css and move them to the head of the document. For fields marked with javascript as the language, script tags will be created instead and placed at the bottom of the body.
When using code fields inside of [extensions]({{ site.link }}/documentation/extensions), the asset attribute is not necessary. Blockstudio will automatically render the code field content as an asset if the language is css or javascript.
{ "$schema": "https://app.blockstudio.dev/schema/extend", "name": "core/*", "blockstudio": { "extend": true, "attributes": [ { "id": "customCss", "type": "code", "label": "Custom css", "language": "css" } ] } } No additional configuration is needed. The above will show a code field for every core/* block in the sidebar and automatically render the content as an asset to the page.
Blockstudio is able to automatically minify all CSS and JS files in a block. Compiled files will be saved to the _dist folder of the block and enqueued when a block is used. When the source file is updated, Blockstudio checks if a compiled file with that timestamp exists, if not, it will be created. The minify library is used for this purpose.
When using SCSS, it is possible to import other SCSS files. Imports are relative to the file they are imported from. @import "modules.scss"; Additionally, custom import paths can be defined using the blockstudio/assets/process/scss/importPaths filter, so files can be imported from other directories without specifying any folder structure. Please note that the .scss extension needs to be present for imports to work properly. Block assets will be recompiled when any of the imported files change.
All scripts (inline and default) in Blockstudio load with type="module" applied to them by default. This means that it is possible to make use of the import syntax for your blocks' scripts without any additional setup. Let's imagine the following code inside a script.js: import { h, render } from "https://esm.sh/preact@10.15.1"; import htm from "https://esm.sh/htm@3.1.1"; import confetti from "https://esm.sh/canvas-confetti@1.6.0"; const html = htm.bind(h); const styles = `display: block; font-family: sans-serif; color: black; background: #f1f1f1; padding: 1rem; border-radius: 0.25rem; margin: 1rem; font-family: sans-serif; `; function App(props) { confetti(); console.log(confetti); return html`<h1 style="${styles}">Hello ${props.name}!</h1>`; } render( html`<${App} name="Hello from Preact!" />`, document.querySelector("#element") ); The example above would create a component using Preact , importing all necessary components and functions from an ESM compatible CDN, in this case esm.sh. While there is nothing wrong with this approach, it has the disadvantage of needing to request the necessary assets from an external site, risking broken scripts if the CDN is down. Blockstudio includes a handy way to download ES Modules to the block, eliminating the need for external ESM CDNs during production.
To download a module to your block directory, simply swap the CDN url. import { h, render } from "blockstudio/preact@10.15.1"; import htm from "blockstudio/htm@3.1.1"; import confetti from "blockstudio/canvas-confetti@1.6.0"; Blockstudio will automatically check changed .js files for imports and download them to the local block directory. The downloaded files will be placed in a modules folder in the block directory. The script file will be rewritten on save to accustom for the locally hosted module, in the example above, the generated scripts would have the following import: import { h, render } from "./modules/preact/10.15.1.js"; import htm from "./modules/htm/10.15.1.js"; import confetti from "./modules/canvas-confetti/1.6.0.js"; When inside the editor, the CDN url will still be used (Blockstudio is using esm.sh) when previewing and working on the block. There you go! The world of NPM is at your fingertips without the boring boilerplate of setting up bundlers and other tools. If you are concerned about performance due to not having a single JS bundle, this article is worth a read. Since the use of ES modules using the above syntax is completely opt-in, there is no need to activate this feature, it is enabled by default.
Normally, modules can be used from CDNs without the version number. In this case, the newest version will always be used. Since Blockstudio is not saving any information into the database, a version number is required when using modules.
Modules are scoped to their blocks. Even if you use the same module with the same version number across multiple blocks, Blockstudio will still download the requested module to the block. This is mainly because blocks should be self-contained units that can easily be shared across other installations or sites. On top of that, the same module will be requested twice if both blocks are present on a page. This is not so much of an issue if the block is loading a very specific module like a slider library. However, if you are creating multiple blocks that rely on the same framework as above (Preact), loading the same module multiple times can become a performance issue. This problem can be solved by using the script-inline.js instead of the script.js file. Blockstudio will rewrite each of the imports to point to the location of the first occurrence of the module if the name and version number are the same.
It is also possible to import CSS files using the same syntax as above. import "blockstudio/swiper@11.0.0/swiper.min.css"; The CSS file will be downloaded to the modules folder and automatically enqueued when the block is used. As long as the version is the same, only a single version of the CSS file will be enqueued, even if it exists in multiple blocks.
Blockstudio will automatically enqueue all files ending with .css, .scss and .js when your block is being used on a page. It is possible to define how assets are being enqueued by using one of the following file names. The * is a wildcard that can be replaced with any string of your choice. - *.(s)css: enqueues as a tag in the editor and on the frontend - *-inline.(s)css: enqueues the contents of the file in an inline
Inline styles and scripts have the big advantage that they are directly rendered as style or script tags inside the page. This can enhance loading times, since it saves extra requests that would have to be made otherwise. - .js files are inlined to the end of the body - .(s)css files are inlined to the end of the head - each file is only being inlined once
Scoped styles are also inlined, but are prefixed with an ID that is unique to each block. Use the bs_get_scoped_class function to add the class to your template.
">
Scope me!
Scope me too!
h1 { color: red; } The above will result in the following scoped style: <div class="bs-62df71e6cc9a"> <h1>Scope me!</h1> <p>Scope me too!</p> </div>
Besides block specific assets, it is also possible to enqueue global assets, which will be available on all pages, regardless if a block is present. Enqueuing a global asset is done by adding the global- prefix to the file name. Any of the suffixes (e.g. -inline) can be used in combination. Possible combinations are: - global-styles.(s)css - global-styles-inline.(s)css - global-styles-editor.(s)css - global-styles-scoped.(s)css - global-scripts.js - global-scripts-inline.js - global-scripts-editor.js - global-scripts-view.js
Block editor assets are enqueued only in the block editor. The block-editor- prefix is used to define block editor assets. - block-editor-styles.(s)css - block-editor-scripts.js
When a block templates returns nothing, Blockstudio will not enqueue any assets for that particular block. This method comes in handy to disable enqueueing when a certain condition is met.
The $attributes or $a variables only give you access to data registered in the blockstudio property. To access information about standard block data like alignment or typography, simply use the $block or $b variables.
If you want to set a default value for a property that is set by WordPress like align, you can do so by adding a default key to the property definition inside the attributes key. { "$schema": "https://app.blockstudio.dev/schema", "name": "blockstudio/block", "title": "Native Block", "icon": "star-filled", "description": "Native Block.", "supports": { "align": ["left", "center", "right"] }, "attributes": { "align": { "type": "string", "default": "center" } }, "blockstudio": true }
There are 8 different operators which can be used: - == - values are equal - != - values are not equal - includes - value is included in reference value - !includes - value is not included in reference value - empty - value is empty - !empty - value is not empty - < - value is smaller than reference value - > - value is bigger than reference value - <= - value is smaller than reference value or equal - >= - value is bigger than reference value or equal
By default, Blockstudio comes with 4 global conditions: post type, post ID, user role and user ID. { "$schema": "https://app.blockstudio.dev/schema", "name": "blockstudio/native", "title": "Native Block", "category": "text", "icon": "star-filled", "description": "Native Blockstudio block.", "supports": { "align": true }, "blockstudio": { "attributes": [ { "id": "message", "type": "text", "label": "My message", "conditions": [ [ { "type": "postId", "operator": "==", "value": "1386" }, { "type": "postType", "operator": "==", "value": "post" } ] ] } ] } } In the example above, the text attribute will only show in the editor if the post ID is 1386 and the post type is post. Please note that the camelCase convention is being used for the type keys. (postType, postId, userRole, userId) If you want to create or conditions instead, simply move the conditions into their own array: { "blockstudio": { "attributes": [ { "id": "message", "type": "text", "label": "My message", "conditions": [ [ { "type": "postId", "operator": "==", "value": "1386" }, { "type": "postType", "operator": "==", "value": "post" } ], [ { "type": "postType", "operator": "==", "value": "jobs" } ] ] } ] } } In the example above, the text attribute will only show in the editor if the post ID is 1386 and the post type is post or the post type is jobs.
It is also possible to set conditions that work between attributes. Instead of setting a type key, simply set the id of the attribute you want to check against. { "blockstudio": { "attributes": [ { "type": "toggle", "id": "copyButton", "label": "Copy Button" }, { "type": "text", "id": "copyButtonText", "label": "Copy Button Text", "default": "Copy", "conditions": [ [ { "id": "copyButton", "operator": "==", "value": true } ] ] } ] } } You can combine global conditions with block condition as you please.
By default, conditions for attributes inside repeaters depend on the attributes of the currently repeated element. { "blockstudio": { "attributes": [ { "type": "toggle", "id": "toggle", "label": "Toggle" }, { "type": "repeater", "id": "repeater", "label": "Repeater", "attributes": [ { "type": "toggle", "id": "toggle", "label": "Toggle" }, { "type": "text", "id": "text", "label": "Text", "conditions": [ [ { "id": "toggle", "operator": "==", "value": true } ] ] } ] } ] } } Since attribute IDs are scoped to the current repeater element, the text attribute inside the repeater will only show if the toggle inside the repeater is set to true. If you want to check against the toggle outside the repeater, you can apply "context": "main" to the condition, and it will show the text attribute if the toggle outside the repeater is set to true. { "blockstudio": { "attributes": [ { "type": "toggle", "id": "toggle", "label": "Toggle" }, { "type": "repeater", "id": "repeater", "label": "Repeater", "attributes": [ { "type": "toggle", "id": "toggle", "label": "Toggle" }, { "type": "text", "id": "text", "label": "Text", "conditions": [ [ { "id": "toggle", "operator": "==", "value": true, "context": "main" } ] ] } ] } ] } }
Attributes can be deactivated on a per-block basis by hovering over the left side of the UI in the sidebar and clicking it when the blue border appears. This comes in handy when an attribute should be disabled temporarily, while leaving the filled content intact. The UI of the attribute will be slightly translucent if it is deactivated. The same principle also works for single elements when using the files attribute type:
The first method filters the attributes in the editor. This is useful if you want to adjust the default value of an attribute or its conditions. The code above will set the default value of the lineNumbers attribute to true and will hide the attribute if the language attribute is not set to css. Keep in mind that this filter is only evaluated when inserting blocks in the editor.
The second method filters the attributes on the frontend. This is useful if you want to adjust the attributes before they are passed to the block template. Keep in mind that the above filter will override any values set in the editor.
Sometimes it might be necessary to use the value of attributes inside your CSS or JS files. Blockstudio provides two helper functions to render the value of attributes as data attributes or CSS variables. Let's imagine a block with the following attribute structure: "blockstudio": { "attributes": [ { "id": "message", "type": "text", "label": "My message" }, { "id": "color", "type": "color", "label": "My color" } ] }
The bs_render_attributes function will render the attributes as data attributes on the element.
Hello
The bs_render_attributes function accepts a second parameter to specify the attributes to render. If no attributes are specified, all attributes will be rendered.
>
{{ a.message }}
The above example will render the following HTML:
Hello
If you don't want to render the output, simply use the bs_attributes function.
Similarly, the bs_render_variables function will render the attributes as CSS variables on the element. This is useful if you want to use the attributes in a CSS file.
{{ a.message }}
Hello
Similarly, the bs_render_variables function accepts a second parameter to specify the attributes to render. If no attributes are specified, all attributes will be rendered.
{{ a.message }}
The above example will render the following HTML:
Hello
If you don't want to render the output, simply use the bs_variables function.
The bs_render_attributes and bs_render_variables functions will convert attribute IDs to kebab case. For example, the IDs myAttribute or my_attribute will be converted to my-attribute.
Instead of supplying different field types like Posts, Users or Terms, Blockstudio allows for a more modular way of populating data right from the block.json. This feature currently works for select, radio,checkbox, color and gradient field types. Data can be populated in three different ways: - Query return results from a post, user or term query (only select, radio, checkbox) - Fetch return results from an external source (only select, radio,checkbox) - Function return results from a custom function - Custom return results from a custom dataset
When choosing the query mode, Posts, Users or Terms can be populated. Getting started is easy, simply add the populate attribute to your block.json and define the type that should be queried for your block. { "$schema": "https://app.blockstudio.dev/schema", "name": "blockstudio/native", "title": "Native Block", "category": "text", "icon": "star-filled", "description": "Native Blockstudio block.", "supports": { "align": true }, "blockstudio": { "attributes": [ { "id": "posts", "type": "select", "label": "Posts", "populate": { "type": "query", "query": "posts" // or "users" or "terms" } } ] } } Following functions are used internally: - posts: get_posts - users: get_users - terms: get_terms
By default, Blockstudio will return following data. - posts: value: post object, label: post_title - users: value: user object, label: display_name - terms: value: term object, label: name The response can be customised with the returnFormat attribute. { "blockstudio": { "attributes": [ { "id": "posts", "type": "select", "label": "Posts", "populate": { "type": "query", "query": "posts", "arguments": { "post_type": "posts", "numberposts": "20", }, "returnFormat": { "value": "id" // only accepts "id" "label": "post_name" // accepts any value found in the respective object } } } ] } }
The query population type allows fetching data from the server by using the fetch option. { "blockstudio": { "attributes": [ { "id": "posts", "type": "select", "label": "Posts", "populate": { "type": "query", "query": "posts", "fetch": true, "arguments": { "post_type": "posts", "numberposts": "20", } } } ] } } Using the settings above, Blockstudio will automatically enable stylisedUi for the attribute and initially fetch the first 20 posts from the get_posts query. Upon typing in a value in the select field, the search value will be used as the s argument in the query, returning posts for the search term. This will also work for the users and terms query types. When fetching users, you might have to adjust the search_columns property in the arguments object to get appropriate results for your query. See the get_users documentation for more information.
Instead of relying on the built-in query functions, it is possible to use a custom function to populate options. This can be useful when you want to return data not covered by the built in query types. { "blockstudio": { "attributes": [ { "id": "postTypes", "type": "checkbox", "label": "Post types", "populate": { "type": "function", "function": "get_post_types" } } ] } }
Similar to the query type, custom arguments can be passed to the function using the arguments key. Internally, Blockstudio uses call_user_func_array, so all arguments have to be passed as an array. { "blockstudio": { "attributes": [ { "id": "postTypes", "type": "checkbox", "label": "Post types", "populate": { "type": "function", "function": "get_post_types", "arguments": [[], "objects"] } } ] } }
Blockstudio will always look for the keys set in the returnFormat object. If not available, it will look for the value and label key in the returned data. If those are not available either, it'll fallback to the first value in the returned array. { "blockstudio": { "attributes": [ { "id": "postTypes", "type": "checkbox", "label": "Post types", "populate": { "type": "function", "function": "get_post_types", "arguments": [[], "objects"], "returnFormat": { "value": "name", "label": "label" } } } ] } }
It is possible to combine options and the data from queries or functions. { "blockstudio": { "attributes": [ { "id": "users", "type": "radio", "label": "Users", "options": { { "value": "administrators", "label": "Administrators", }, { "value": "editors", "label": "Editors", } } "populate": { "type": "query", "query": "users", "position": "before" // this will add the populate options before the static options (defaults to "after") } } ] } }
Instead of relying on one of the built-in ways to populate options with data, it is possible to create custom datasets that can be easily reused within fields. Adding custom data is done using the blockstudio/blocks/populate filter. Then call the dataset inside the block.json file. { "blockstudio": { "attributes": [ { "id": "posts", "type": "select", "label": "Posts", "populate": { "type": "custom", "custom": "customData", } } ] } }
It is possible to fetch data from an external source using the fetch type. The search will be appended to the searchUrl argument. { "name": "blockstudio/fetch", "title": "Fetch", "description": "Fetch field", "blockstudio": { "attributes": [ { "id": "select", "type": "select", "label": "Select", "multiple": true, "populate": { "type": "fetch", "arguments": { "urlSearch": "https://fabrikat.io/streamline/wp-json/wp/v2/posts?search=" }, "returnFormat": { "value": "id", "label": "title.rendered" } } } ] } }
Of course, blocks without dynamic data are kinda boring! Attributes allow you to add data to your blocks and are comparable to custom fields that you know from solutions like ACF or Metabox. Custom attributes are registered using the blockstudio property inside the block.json file. { "$schema": "https://app.blockstudio.dev/schema", "name": "blockstudio/native", "title": "Native Block", "category": "text", "icon": "star-filled", "description": "Native Blockstudio block.", "supports": { "align": true }, "blockstudio": { "attributes": [ { "id": "message", "type": "text", "label": "My message" } ] } } And that's it! You've just registered your first custom attribute:
To create a new code snippet, simply create a new folder and place an init.php file in it. Blockstudio will always execute that file. For more information on the init file, check the [Initialization]({{ site.link }}/documentation/initialization/) page.
Folders marked as code snippets will also process and enqueue styles and scripts. To enqueue global styles and scripts, use the global [prefix for the asset name]({{ site.link }}/documentation/assets/registering/#global). Assets inside code snippet folders also support all [processing]({{ site.link }}/documentation/assets/processing/).
The above technique can also be used on directories outside of the folder where all blocks are stored. For example, let's say you want to store global assets like styles and scripts inside your theme. Simply create a new Blockstudio instance using the [init method]({{ site.link }}/documentation/registration/#instances). add_action('init', function () { Blockstudio\\Build::init([ 'dir' => get_template_directory() . '/assets' ]); }); Now, simply follow the setup instructions inside the to register a code snippet inside the assets folder.
Inner blocks allow you to insert additional blocks to your blocks. Under the hood, Blockstudio is using the InnerBlocks component. Please note that it is only possible to use one InnerBlocks component per block, this is a WordPress limitation.
To use InnerBlocks, you need to add the InnerBlocks component to your block. or The composition is up to you. You can nest the InnerBlocks component as deep as you want or just use it by itself.
The templateLock prop allows you to define if the template can be modified or not. - contentOnly: prevents all operations. Additionally, the block types that don't have content are hidden from the list view and can't gain focus within the block list. Unlike the other lock types, this is not overrideable by children. - all: prevents all operations. It is not possible to insert new blocks. Move existing blocks or delete them. - insert: prevents inserting or removing blocks, but allows moving existing ones. - false: prevents locking from being applied to an InnerBlocks area even if a parent block contains locking.
The renderAppender prop allows you to define the block appender type. - default: display the default block appender, typically the paragraph style appender when the paragraph block is allowed. - button: display a + icon button as the appender.
Blockstudio supports context for the InnerBlocks component. This allows you to pass data from the parent block to the child blocks. While it uses the WordPress core context mechanism under the hood, Blockstudio provides a more convenient API. Instead of having to define all attributes that should be passed in the context separately, you can simply subscribe to all attributes of a parent block. Let's say you have a container block setup like so: { "$schema": "https://app.blockstudio.dev/schema", "name": "blockstudio/container", "title": "Container", "category": "design", "icon": "star-filled", "description": "Container block.", "blockstudio": { "attributes": [ { "id": "full-width", "type": "toggle", "label": "Makes section full width" } ] } } Inside your child block, simply use the usesContext property with the parent block name to gain access to all parent attributes in your block template. { "$schema": "https://app.blockstudio.dev/schema", "name": "blockstudio/element", "title": "Element", "category": "design", "icon": "star-filled", "description": "Element block.", "usesContext": ["blockstudio/container"], "parent": ["blockstudio/container"], "blockstudio": true }
The $context and $c variables are available inside your block templates to access all parent attributes. Child blocks will automatically reload in the editor if parent data changes.
{{ container['full-width'] ? 'is full width' : 'no full width' }}
InnerBlocks has to render a wrapper element around its children inside the editor, however, it can be removed from the frontend by using blockstudio/blocks/components/innerblocks/frontend/wrap filter.
If you want to return early from the render method of your block, you can check if the $innerBlocks (it contains the content) variable is empty. This comes in handy if you don't want to render anything if there are no inner blocks. Since is will always render an empty paragraph tag, you can use the strip_tags function to check if the content is empty.
The MediaPlaceholder component provides a placeholder to add media items to a block. The same component is being used in the core/image block. It is based on the React component with the same name.
To use MediaPlaceholder in its most basic form, you need to add the appropriate attribute to your block.json and add the MediaPlaceholder component to your block. { "$schema": "https://app.blockstudio.dev/schema", "name": "blockstudio/media-placeholder", "title": "Native Block", "category": "text", "icon": "star-filled", "description": "Native Blockstudio MediaPlaceholder block.", "blockstudio": { "attributes": [ { "id": "media", "type": "files", } ] } } or or {{ fn('wp_get_attachment_image', file.ID) }} Once a media file has been selected, the MediaPlaceholder component will not be rendered anymore.
A string passed to FormFileUpload that tells the browser which file types can be upload to the upload window the browser use e.g.: image/* or video/*. More information about this string is available in here.
Array with the types of the media to upload/select from the media library. Each type is a string that can contain the general mime type e.g.: image, audio, text, or the complete mime type e.g.: audio/mpeg, image/gif. If allowedTypes is unset, all mime types should be allowed.
If true, the Drop Zone will not be rendered. Users won't be able to drag & drop any media into the component or the block. The UI controls to upload the media via file, url or the media library would be intact.
If true, only the Drop Zone will be rendered. No UI controls to upload the media will be shown. The disableDropZone prop still takes precedence over dropZoneUIOnly – specifying both as true will result in nothing to be rendered.
If true, the property changes the look of the placeholder to be adequate to scenarios where new files are added to an already existing set of files, e.g., adding files to a gallery. If false, the default placeholder style is used.
The RichText component allows rendering an editable input inside the editor and a static HTML Element in the frontend. It is based on the RichText component WordPress provides.
To use RichText in its most basic form, you need to add the appropriate attribute to your block.json and add the RichText component to your block. { "$schema": "https://app.blockstudio.dev/schema", "name": "blockstudio/rich-text", "title": "Native Block", "category": "text", "icon": "star-filled", "description": "Native Blockstudio RichText block.", "blockstudio": { "attributes": [ { "id": "myRichText", "type": "richtext", } ] } } or You can use an unlimited number of RichText components in your block.
Whether to preserve white space characters in the value. Normally tab, newline and space characters are collapsed to a single space. If turned on, soft line breaks will be saved as newline characters, not as line break elements.
By default, Gutenberg has to create a wrapper around blocks to make them interactive inside the editor. This can become problematic for some block types like containers or columns, since the markup between editor and frontend will be different. The useBlockProps hook makes it possible to mark an element inside the block template as the root element, thus creating markup parity between editor and frontend.
Simply add the useBlockProps attribute to the root element of your block.
Blockstudio will automatically combine classes and attributes from the editor (alignment classes etc.) along with whatever is defined in the block templates. get_block_wrapper_attributes is being used under the hood for that.
Since there can only be one root element in a block template, when using useBlockProps, the block template has to have a single root element. For example, the following will not work and Gutenberg will create a wrapper around the template.
First root element.
Second root element. Similarly, useBlockProps can only be used once per block template.
Blockstudio converts the block template to valid React code inside Gutenberg using html-react-parser. This comes with the limitation that the @ character can't be used inside an HTML attribute name. Libraries like Alpine.js use the @ characters to define directives on HTML elements. Inside Gutenberg, this @click attribute will be stripped from the element. (it will still work on the frontend, of course) To combat this issue, you can use the alternative syntax Alpine.js provides. The above will work inside Gutenberg and on the frontend.
Blockstudio can be added to projects using composer with the private-composer-installer package. It allows you to install packages from private URLs within composer.json.
It is good practice to store all your credentials and private keys in an .env file. If you haven't done so already, create a .env file in the root of your project and add your Blockstudio license key BLOCKSTUDIO_KEY=your-blockstudio-license-key
When blocks are used inside a loop (for example, a Query block), the block is executed once for each iteration of the loop. Blockstudio provides handy shortcuts for accessing the current loop and outer context.
This is the data of current element inside the loop:
This is the data the current post:
This is the data of current element inside the loop: {{ block.context.postId }} {{ block.context.postType }}
This is the data the current post: {{ block.postId }} {{ block.postType }}
The default value for attributes will be used in the editor. If you prefer to preview the block with different data, you can use the example key. Keep in mind that the example data will be also be used when [previewing]({{ site.link }}/documentation/environment/#preview) the block
Blockstudio includes a full code editor, capable of editing and creating blocks all within the WordPress admin area. The current feature set includes: - Creating a custom plugin if no block source has been found - Creating new files - Editing files - Renaming files - Deleting files - Creating new blocks - Deleting blocks - Importing data - Exporting data - Editing assets - Adding assets - Deleting assets - Live preview of blocks
Just like many other popular online IDEs, Blockstudio is using the Monaco Editor for code editing. Since VS Code is powered by this very same editor, VSC users should feel right at home with it. Besides that, the editor interface is making heavy use of Gutenberg components, ensuring similarities in look and behaviour to the WordPress admin area and Gutenberg itself.
Although there are checks in place that will prevent the saving of blocks in case the code creates a critical error on your site, it is advisable to either use the editor locally or on a staging environment. Keep in mind that the Blockstudio editor is not much different from using an editor on your computer, as it directly modifies the actual files in your block directory.
This will activate the editor (accessible in the WordPress admin menu) for the users with the ID of 1 and 19. This will activate the editor for all users with the role of administrator and editor.
Assets that were registered using wp_enqueue_scripts, admin_enqueue_scripts or enqueue_block_editor_assets can be added. Similarly, it is also possible to add additional styles for just a single block using the following configuration inside the block.json: { "name": "blockstudio/native", "title": "Native Block", "category": "text", "icon": "star-filled", "description": "Native Blockstudio block.", "blockstudio": { "editor": { "assets": ["stylesheet-handle"] } } }
The introduction of the editor is an exciting development for Blockstudio users and WordPress developers working with Gutenberg. Now that there is an accompanying interface for block directories, importing custom blocks from the library directly into your theme will be possible in the near future. On top of that, more work will be put into removing the barriers for integrated block development within WordPress. Tailwind, SCSS and a neat solution for web components as blocks are on the cards, so stay tuned!
Since 5.2, it is possible to edit blocks directly from Gutenberg. By clicking the "Edit with Blockstudio" button in the sidebar, the editor will open in a separate window and allow you to make changes to the block itself or its assets and preview them inside Gutenberg. Editing blocks using this method has one advantage over the built-in editor preview that Blockstudio provides: the same block inserted multiple times with different settings can be previewed at once. Please note that the editor needs to be active in order for the preview button to show up.
Blockstudio uses the Tailwind CDN version to compile your Tailwind classes when developing in the editor. Upon saving a block, all used Tailwind classes are saved to a file and automatically enqueued on the frontend and block editor.
If you need to recompile Tailwind classes after changes in your templates not made with the Blockstudio editor, you can do so by simply saving the settings again. This will recompile the Tailwind classes and save them to the file again.
Sometimes it is necessary to show content in your blocks exclusively when it is being rendered inside the Gutenberg editor and not on the frontend. This technique is very useful to provide users with helpful information and placeholders if the necessary data hasn't been input yet.
This content is only going to be rendered inside the editor. This content is only going to be rendered on the frontend. This content is only going to be rendered inside the editor. This content is only going to be rendered on the frontend.
Blockstudio adds another environment variable that will come in handy for block developers. $isPreview allows you to conditionally render content inside the block preview window when hovering over a block inside the block inserter. This content is only going to be rendered inside the block preview. This content is only going to be rendered on the frontend and editor. This content is only going to be rendered inside the block preview. This content is only going to be rendered on the frontend and editor.
Extensions allow you to extend any registered block in your WordPress installation using a simple .json based API, which is similar to the block.json configuration. This includes core, third-party and blocks made with Blockstudio. All [field types]({{ site.url }}/documentation/attributes/field-types) can be added to any block, with the data being saved to the block's attributes. The complete feature set of attributes like [filtering]({{ site.url }}/documentation/attributes/filtering), [disabling]({{ site.url }}/documentation/attributes/disabling), [conditional logic]({{ site.url }}/documentation/attributes/conditional-logic) and [populating options]({{ site.url }}/documentation/attributes/populating-options) are available for extensions.
Extended values can now be utilized in more versatile ways: - class: Add a class using the attribute value to the block. - style: Add inline styles using the attribute value to the block. - attributes: Set any attribute value directly on the block, enhancing its functionality or accessibility. - data attributes: Use extension values to set specific data attributes, enabling more complex interactions and dynamic content adjustments.
Imagine the following scenario: you want to add a custom class select field to every core block. All you need to do is add this .json file anywhere in your block folder, and it will be automatically registered as an extension. { "$schema": "https://app.blockstudio.dev/schema/extend", "name": "core/*", "priority": 10, "blockstudio": { "extend": true, "attributes": [ { "id": "customColor", "type": "select", "label": "Custom color", "options": [ { "value": "#f00", "label": "Red" }, { "value": "#00f", "label": "Blue" } ], "set": [ { "attribute": "style", "value": "color: {attributes.customColor}" } ] } ] } } This will add a customColor field to every core block, allowing you to set the color of the block's text. Let's break down the configuration from top to bottom:
Since there is no rendering template, an extension doesn't need a matching index file and thus doesn't need to be named block.json. This allows storing all extensions conveniently in a single folder.
The name property of the configuration object defines which blocks should be extended. There are three options depending on your needs: - blockName: A single block name, e.g. core/paragraph. - [blockNameA, blockNameB]: An array of block names, e.g. ["core/paragraph", "core/heading"]. - core/*: A wildcard to extend all blocks of a certain type, e.g. core/*.
By default, extensions will render in its own tab in the block inspector. If you want to move the extensions to a different position, you can use the group property. The following values are currently available: - settings: The default position. - styles: The styles tab. - advanced: The advanced tab. { "$schema": "https://app.blockstudio.dev/schema/extend", "name": "core/*", "blockstudio": { "extend": true, "group": "styles", "attributes": [] } }
The priority property allows you to define the order in which extensions are rendered. Extensions with a higher priority will be rendered later. This is useful if you want an extension to appear before another one.
For more information on how attributes work, please refer to the [attributes section]({{ site.url }}/documentation/attributes/field-types). The only thing you need to know about attributes inside extensions is the set property. It is used to apply the value of the attribute to one of the [types]({{ site.url }}/documentation/extensions#extension-types) of the block. For example, the following configuration will set the style attribute of the block to color: red or color: blue depending on the value of the select field. { "$schema": "https://app.blockstudio.dev/schema/extend", "name": "core/*", "blockstudio": { "extend": true, "attributes": [ { "id": "customColor", "type": "select", "label": "Heading color", "options": [ { "value": "#f00", "label": "Red" }, { "value": "#00f", "label": "Blue" } ], "set": [ { "attribute": "style", "value": "color: {attributes.customColor}" } ] } ] } }
Dot notation inside curly braces is used to get the value of an attribute. It is also possible to get nested values when attributes are not returning a single value, for example, when setting the return format to both on field types with options. { "attributes": [ { "id": "customColor", "type": "select", "label": "Heading color", "options": [ { "value": "#f00", "label": "Red" }, { "value": "#00f", "label": "Blue" } ], "returnFormat": "both", "set": [ { "attribute": "style", "value": "color: {attributes.customColor.value}" }, { "attribute": "data-color", "value": "{attributes.customColor.label}" } ] } ] } The example above will set the style attribute to color: #f00 or color:#00f and the data-color attribute to Red or Blue depending on the value. For now, only values from the current attribute can be used inside set definitions. The ability to use values from other attributes might be added in the future.
Blockstudio enables you to create custom WordPress blocks with nothing but PHP using the WordPress block.json format. It greatly simplifies the developer experience in regard to block registration, lazy loading assets, and custom fields.
There is no secret sauce. Blockstudio is built on top of core WordPress features and Gutenberg components. It is designed to be a lightweight abstraction layer that helps you get started with custom blocks quickly.
When creating dynamic blocks using nothing but WordPress, there is duplication of logic between PHP and JS code. There are discussions about solving this issue, including hydrating blocks on the client side (not good for SEO) or introducing a JSX parser for PHP (complex). Blockstudio takes a different approach. You use PHP to write your block templates and sprinkle in interactivity like or using JSX-like tags. Inside the editor, those tags will be replaced with their React counterparts. On the frontend, the tags will be replaced with HTML content. This way, you get the best of both worlds: a server-side rendered block with great interactivity in the editor.
While WordPress supports a file-based approach using the WPDefinedPath string type, it is also possible to link assets or templates from other destinations. Blockstudio doubles down on the file-system as the primary (and only) way of registering blocks. This decision was not made to restrict, but to set a block structure in place that can't be argued with. For example, if a style.css or script.js file is found in the same folder as a block.json, then you can be sure that it belongs to this exact block only.
Blockstudio just works. It is designed to remove friction when composing custom blocks and doesn't come in your way while doing so. Want to use Twig for your template? Simply rename the file extension. Need to include some CSS? Create a style.css file. Blockstudio will automatically enqueue it whenever the block is being used on your page, even when it is used outside the post_content with the [rendering function]({{ site.url }}/documentation/rendering-blocks). Do you have feature requests or questions? Message us.
Besides PHP hooks, Blockstudio also provides JavaScript events to manipulate values and blocks inside the editor. Since events are tied to block names and attribute IDs, we will use namespace/my-block as the name in the following examples. Of course, you will need to replace it with your own block name.
This event is dispatched when the blocks are loaded. It can be used to initialise code once the block has loaded in the editor. Alternatively, you can listen to all blocks.
This event is dispatched when the blocks are rendered. It can be used to initialise code once the block has rendered in the editor. Alternatively, you can listen to all blocks.
This event is dispatched when an attribute is updated. For this example we will imagine a text field with the ID of text. Alternatively, you can listen to all attribute updates.
Since IDs are not necessarily unique across attributes (for example inside and outside repeaters), you can specify a key inside the attribute object to uniquely identify the attribute when using events. { "blockstudio": { "attributes": [ { "id": "repeater", "type": "repeater", "label": "Repeater", "attributes": [ { "id": "text", "type": "text", "label": "Text" }, { "id": "repeater", "type": "repeater", "label": "Repeater", "textButton": "Add repeater", "attributes": [ { "key": "my-unique-key", "id": "text", "type": "text", "label": "Text" } ] } ] } ] } } With the setup above, listening to the key instead of the ID will only trigger the event when the text field inside the repeater is updated. document.addEventListener( "blockstudio/namespace/my-block/attributes/my-unique-key/update", function (event) { const { attribute, attributes, block, value, clientId, repeaterId } = event.detail; } );
This filter allows you to adjust the path of the block folder. By default, this is blockstudio inside the currently active theme. Alternatively it is possible to [create a new instance]({{ site.link }}/documentation/registration/#instances) for multiple source directories.
This filter allows you to adjust the data of the block.json file before it is registered. The above code would change the icon of all blocks starting with marketing.
This filter allows you to adjust the attributes of a block before the block is registered. See [Filtering]({{ site.link }}/documentation/attributes/filtering/) for more information.
This filter allows you to adjust the attributes of a block before it is rendered. See [Filtering]({{ site.link }}/documentation/attributes/filtering/) for more information.
This filter allows you to add custom data to the options of a checkbox, select or radio field. See [Populating options]({{ site.link }}/documentation/attributes/populating-options/) for more information.
Block templates will only be executed when the block is rendered. This is enough for most blocks; however, sometimes you need to execute code during an earlier stage of execution. For example, you may want to register a new post type or do some other type of setup unrelated to the block. To do this, you can add a PHP file that starts with init-, like init.php or init-post-types.php to your block directory. This file is executed during the init action. For more information on this specific stage, see the WordPress documentation. Any init.php file that is found within the block directory will be executed, regardless if it is part of a block context or not. This makes it perfect for organizing code snippets that are not related to any certain blocks.
Since version 3.1.0, Blockstudio includes a library of prebuilt elements. Of course, these elements are making use of all the features that are available to Blockstudio users. In fact, the library is actively being used to dogfood our own product, helping us fast-track development and ensure a good developer experience for everyone. The library is a first step in making Blockstudio more available as a general collection of ready-to-use blocks for non developers, all the while being built with the most advanced block framework for WordPress. More blocks are actively being developed!
Blockstudio blocks are dynamic, meaning that they must be rendered on the server to be displayed in Gutenberg. This rendering is facilitated by a fetch call to the Blockstudio block renderer. Upon first opening the editor, each block fetches its respective template, which results in multiple requests depending on the number of blocks present on the page. On some hosts, this can cause timeouts and errors while loading other essential assets needed for the editor to function properly.
Rendering template loading can be disabled by setting the disableLoading key in block.json to true. A placeholder will be rendered in place of the block template. All field settings will still be available when opening the sidebar. { "name": "blockstudio/native", "title": "Native Block", "category": "text", "icon": "star-filled", "description": "Native Blockstudio block.", "blockstudio": { "blockEditor": { "disableLoading": true } } } }
Blockstudio includes the possibility to override blocks on a very granular level. This can be useful for shared block libraries when you want to change the behavior of a block without having to create a new block from scratch.
To get started, set the name of the block you want to override as the name key and set the override key to true. { "name": "blockstudio/my-block", "title": "My overridden block title", "blockstudio": { "override": true } } When the override property is set to true, the data from the new block.json will be merged with the data from the original block. This means that you can override any property of the original block.
Attributes can either be overridden by id or added additionally. For example, let's imagine that we want to override the width attribute of a block and add a new height attribute. { "name": "blockstudio/my-block", "title": "My overridden block title", "blockstudio": { "override": true, "attributes": [ { "id": "width", "type": "number", "default": 280, "label": "Override width" }, { "id": "height", "type": "number", "default": 280, "label": "New height" } ] } } When an attribute with the same id is found, the data will be merged with the original attribute. Attributes which are not found will be added to the block. Important: When overriding attributes, you have to provide the type of the original attribute, even if it stays the same.
Asset files will be replaced when the file name matches. For example, if a style.css file exists in the original block, it will be replaced by the style.css file in the overriding block. Files with names that do not match the original block will be added to the block.
Twig templates can be overridden on a more granular level using the block feature in Twig. Let's imagine the following rendering template for a fictional image block:
Images:
Instead of replacing the whole template, we can use Twigs extends feature to override certain parts of the template while keeping the rest intact. {% block image %} <figure> <img src="{{ image.url }}" class="image" /> <figcaption>{{ image.caption }}</figcaption> </figure> {% endblock %}
Since block templates are just normal PHP files, you can use any PHP code you want in them. However, since blocks don't have access to the global $post variable inside the editor, get_the_ID() and other functions that depend on the variable will only work on the frontend. Blockstudio provides four different ways to access the current post ID in the editor and the frontend. echo $post_id; echo $postId; echo $block['postId']; echo $b['postId']; {{ post_id }}; {{ postId }}; {{ block.postId }}; {{ b.postId }};
By default, blocks won't be refreshed when you save or update a post. Thus, old data might be displayed in the editor after saving. To fix this, you can use the refreshOn key inside your block.json to tell Blockstudio to refresh the block when a post is saved. { "name": "blockstudio/native", "title": "Native Block", "category": "text", "icon": "star-filled", "description": "Native Blockstudio block.", "blockstudio": { "refreshOn": [ "save" ] } }
Gutenberg allows previewing blocks when hovering over the block in the fixed block inserter. To enable the preview of blocks made with Blockstudio, simply set the example key to an empty object. { "name": "blockstudio/native", "title": "Native Block", "category": "text", "icon": "star-filled", "description": "Native Blockstudio block.", "example": {}, "blockstudio": true }
It is also possible to provide structured data in the example key, so any custom attributes are correctly being rendered. { "name": "blockstudio/native", "title": "Native Block", "category": "text", "icon": "star-filled", "description": "Native Blockstudio block.", "example": { "attributes": { "title": "This title will be shown in the preview" } }, "blockstudio": true }
If your block relies on InnerBlocks, it is possible to provide a template for the InnerBlocks. { "name": "blockstudio/native", "title": "Native Block", "category": "text", "icon": "star-filled", "description": "Native Blockstudio block.", "example": { "innerBlocks": [ { "name": "core/paragraph", "attributes": { "content": "This is the default InnerBlocks block." } } ] }, "blockstudio": true }
Composing your own blocks with Blockstudio is extremely easy. The plugin will look for a blockstudio folder within your currently activated theme. Inside it, all subfolders that contain a block.json file with a blockstudio key will be registered.
So far, the custom block is not going to be registered since it is missing a template. To fix that just create an index.php or index.twig in the same folder as your block.json file and its contents will automatically be rendered when your block is used.
Twig is a flexible, fast, and secure template engine for PHP. It allows developers to write concise and readable code in templates, enhancing productivity and maintainability. Twig supports a variety of features designed to make templating both powerful and straightforward, making it ideal for projects that require robust, reusable templates.
Blocks can be registered conditionally using the conditions key. It supports all the global variables which are also available for attributes. See all conditions The following example will only register the block if the current post type is a page: { "blockstudio": { "conditions": [ [ { "type": "postType", "operator": "==", "value": "page" } ] ], } }
Custom SVGs icons for blocks can be registered like so: { "blockstudio": { "icon": "" } } WordPress doesn't allow for custom SVG icons inside its own block.json icon key, only Dashicons IDs are allowed here.
Blockstudio is using a React element parser to render the icon element, so it is possible to use HTML inside the icon string. { "blockstudio": { "icon": "
By default, Blockstudio will recursively look through the blockstudio folder inside your current theme or child theme for blocks. You can change that behaviour in two ways.
// Custom path within your theme. add_filter('blockstudio/path', function () { return get_template_directory() . '/blocks'; }); // Custom path within a plugin. add_filter('blockstudio/path', function () { return plugins_url() . '/my-custom-plugin/blocks'; });
If the above options are not enough, it is possible to initiate the Blockstudio\\Build class on various folders of your choice: add_action('init', function () { Blockstudio\\Build::init([ 'dir' => get_template_directory() . '/client-blocks' ]); });
Metadata can be filtered before the block is being registered using the blockstudio/block/meta filter: The example above is being used internally to give all Blockstudio library elements the same icon.
With Gutenberg becoming the prominent instrument in creating easily editable websites for clients, it makes sense to create all necessary website areas as blocks. While this approach will cater to most, advanced users and specific use cases might need to use those existing blocks outside the editor. Blockstudio provides two specific functions for exactly this use case: - bs_render_block: immediately renders the block to the page (no "echo "needed) - bs_block: needs an "echo" to be rendered to the page. This function is useful when nesting blocks. All Blockstudio specific features like inline styles, scripts, and scoped styles are supported when using the render functions.
In its simplest form, the function accepts a single value which is the ID of the block that should be rendered on the page. bs_render_block('blockstudio/cta');
To render the block with custom data, an array needs to be used in place of a single value for the first parameter. The value in the data key will be passed to the $attributes and $a variable inside your block template. bs_render_block([ 'id' => 'blockstudio/cta', 'data' => [ 'title' => 'My title', 'subtitle' => 'My subtitle', ], ]);
It is also possible to create multiple content slots by simply making the $content variable an associative array and calling it approriate keys in the bs_block function.
To improve development with autocomplete and validation in your IDE, Blockstudio introduces its own schema: View Blockstudio schema The Blockstudio schema is an (always up-to-date) copy of the WordPress core block.json schema with an additional blockstudio property that houses all plugin features like field schemas without interfering with potential future core properties
Simply add a $schema key with a value of https://app.blockstudio.dev/schema to your block.json and your IDE should start to give you hints about Blockstudio specific properties. { "$schema": "https://app.blockstudio.dev/schema", "name": "blockstudio/native", "title": "Native Block", "category": "text", "icon": "star-filled", "description": "Native Blockstudio block.", "blockstudio": true }
Blockstudio includes a powerful settings API, that allows setting options via a blockstudio.json file inside your theme folder and/or filters. Additionally, allowed users are able to change the settings visually inside the admin area.
If a blockstudio.json file is present inside your theme folder, it will be used to set the default options for the current site. A JSON schema is available to validate the file and help with autocompletion when used in an IDE. The following properties are available. { "$schema": "https://app.blockstudio.dev/schema/blockstudio", "users": { "ids": [], "roles": [] }, "assets": { "enqueue": true, "minify": { "css": false, "js": false }, "process": { "scss": false } }, "editor": { "formatOnSave": false, "assets": [], "markup": false }, "library": false }
Alternatively you can use the blockstudio/settings/${setting} filter to set options via PHP for more flexibility. add_filter('blockstudio/settings/assets/enqueue', '__return_false'); add_filter('blockstudio/settings/editor/formatOnSave', '__return_true'); add_filter('blockstudio/settings/library', '__return_true'); Options set via the blockstudio/settings/${setting} filter will override the ones set via the blockstudio.json file. Both methods can be used together.
Allowed users from the blockstudio/settings/users/ids and blockstudio/settings/users/roles filters are able to change the settings visually inside the admin area. If the Save as JSON checkbox is checked, the settings will be saved to the JSON file, otherwise they will be saved to the database into the options table. Settings set via filters will be grayed out and disabled inside the admin area.
The Block Transforms API allows you to define ways in how to transform blocks. Blockstudio currently supports three different types: - block to block: transform a block into another block - enter: insert a block when the user enters text and presses Enter - prefix: insert a block when the user enters text and presses Space
Block to block transforms allow you to transform a block into another block. { "$schema": "https://app.blockstudio.dev/schema", "name": "blockstudio/transforms", "title": "Native Transforms", "icon": "star-filled", "description": "Native Blockstudio Transforms.", "blockstudio": { "transforms": { "from": [ { "type": "block", "blocks": [ "blockstudio/transforms-2", "blockstudio/transforms-3" ] } ] }, "attributes": [ { "id": "text", "type": "text", "label": "Text" } ] } } When transforming into the new block, the attributes of the old block will be passed to the new block. In the example above, the text attribute will be passed to the new block. The same goes for all InnerBlocks content. It is important to note that the attribute types have to be the same for the block being transformed into and the block being transformed from. For example, if the block being transformed into has an attribute with the ID of text but the type of number, it won't appear in the transform list, as this would cause a type mismatch and rendering error. The number of attributes between the two blocks doesn't have to be the same.
Enter transforms allow you to insert a block when the user types certain content and then presses the Enter key. { "blockstudio": { "transforms": { "from": [ { "type": "enter", "regExp": "/^-{3,}$/" } ] } } } In the example above, the block will be inserted when the user types three or more dashes (-) and then presses Enter.
Prefix transforms allow you to insert a block when the user types certain content and then presses the Space key. { "blockstudio": { "transforms": { "from": [ { "type": "prefix", "regExp": "???" } ] } } } In the example above, the block will be inserted when the user types three question marks (?) and then presses Space.
The Block Variation API allows you to register custom variations for existing blocks. Let's imagine a block that should have two different variations depending on the value of a simple select field. { "$schema": "https://app.blockstudio.dev/schema", "name": "blockstudio/variations", "title": "Native Variation", "description": "Native Blockstudio Variation block.", "icon": "star-filled", "variations": [ { "name": "variation-2", "title": "Native Variation 2", "description": "Native Blockstudio Variation block 2.", "attributes": { "select": { "value": "variation-2" } } } ], "blockstudio": { "attributes": [ { "id": "select", "type": "select", "label": "Select", "options": [ { "value": "variation-1", "label": "Variation 1" }, { "value": "variation-2", "label": "Variation 2" } ], "default": { "value": "variation-1" } } ] } } The above will create three blocks in the inserter, the main one, and two variations.
Attributes will automatically render the appropriate input in the sidebar. Since variation blocks depend on specific attribute values, you might want to hide those fields from the sidebar. To do so, you can use the hidden option. { "blockstudio": { "attributes": [ { "id": "select", "type": "select", "label": "Select", "hidden": true, "options": [ { "value": "variation-1", "label": "Variation 1" }, { "value": "variation-2", "label": "Variation 2" } ] } ] } }