[placeholder]

How We Improved Our Core Block Editing Experience Without It Feeling Like Technical Debt

How We Improved Our Core Block Editing Experience Without It Feeling Like Technical Debt

Note: All tools, unless specified otherwise, are internal tools only used at Squarespace.

Squarespace’s core product is its site builder, which allows our millions of customers to build their unique sites. Through our attempts at building the best site builder for our customers, we’ve come to use a conceptual model that separates and structures components within the platform.

All sites are created from our templates, which are basic, pre-built sites, allowing customers to further customize their sites. Within each template, a customer is able to create multiple pages, where each page consists of individual sections, portions of a page that make it easier to organize content on any given page. The content, like text and buttons, that lives within sections are called blocks — small components that contain distinct types of content that customers can drag and drop onto their site pages. There are currently around 40 types of blocks available within the site builder that allow users to add custom content like text, buttons, videos, maps, and more.

We’ve learned that the separation of templates vs. pages vs. sections vs. blocks has proven to be an effective approach for giving as much freedom and a positive editing experience to our customers. This conceptual model has quickly turned blocks into an integral feature of the platform due to how highly visible the feature is to our customers.

My team is the Core Layout team, and we are responsible for building and maintaining the core editing experience for sites on Squarespace. This means that we own all the features that focus on page, section, and block editing on the site builder.

As a team that believes in Squarespace’s core pillar that “design is not a luxury,” earlier this year, the Core Layout team addressed the largest shortcoming of the block editors: they didn’t match the modern Squarespace product design language.

The block editors were built with the legacy YUI framework nearly 10 years ago when the company was working with a significantly different design system. At the time, YUI was the best framework to solve many of the technical challenges faced by frontend teams at Squarespace. But a decade later, we’ve realized that YUI hasn’t been able to evolve to continue supporting our platform’s needs.

Legacy block editors for the Image and Button blocks.

Legacy block editors for the Image and Button blocks.

Unifying the Squarespace Brand Through Our CMS

My team and I decided to unify the block editing experience with our section and page editing experiences, so that customers can rely on a consistent look and feel with all of the tools that they use regularly. By the time we considered updating the block editing experience, Squarespace designers and developers had established our design system, Rosetta. We wanted our new block editors to use Rosetta while enabling our team to build these new block editors with modern frontend technology like React and the latest JavaScript standards like ES2020+.

Many of the features that are made available to customers are built using an internal framework called JSF, or JSON Schema Forms, which makes it incredibly easy for developers to render semantically-described visual compositions by using either pre-built Rosetta UI components or custom-built components using React. JSF makes the process of composing different views, features, and experiences with React a lot faster and offloads the work of developers importing or redefining commonly used React components in multiple places.

Though this all sounds great on paper — less coding for developers — another problem arose: the time saved in importing and defining custom React components was lost again to the process of typing out the instructions for JSF to know how to render compositions. These instructions are called uiSchemas, which are JSON files that can grow at an exponential rate the more complicated a view becomes.

With the hundreds of different views and experiences Squarespace offers within its site builder, it quickly became clear that manually writing extremely long JSON files would be a burden to developers.

We didn’t want developers to write hundreds or even thousands of lines of JSON — might as well go back to writing custom React components — so a number of Squarespace developers built an internal tool called uischema-builder, which is a module that exposes declarative functions that generate customized uiSchemas for developers. Through the use of uischema-builder, developers are able to generate massive uiSchemas without interfacing with tons of JSON by using a straightforward API.

uischema-builder makes it easy to generate uiSchema, which is then used as instructions for JSF to know how to render visual compositions throughout the site builder.

uischema-builder makes it easy to generate uiSchema, which is then used as instructions for JSF to know how to render visual compositions throughout the site builder.

JSF is used virtually everywhere throughout our frontend across different products, so having a tool like uischema-builder significantly improves the way that developers build features for Squarespace.

What is a uiSchema?

Before we move onto the rest of this post, let’s take a moment to really understand what a uiSchema is and why we have this concept in our codebase.

A uiSchema follows the concept of a schema in that it defines a certain structure that is meant to be followed. More specifically, at Squarespace, a uiSchema is a JSON file that describes the look and structure of visual components that will be rendered onto the screen, hence the ui in uiSchema. So if I wanted to render an input field, a dropdown, and then a disclosure on my screen in that exact order, I could write a uiSchema that describes that exact visual order and then pass it over to JSF to be rendered onto my screen.

This is a uiSchema that specifies to render a string with a placeholder with the text ‘Learn More’ and it’s value variable name is buttonText.

This is a uiSchema that specifies to render a string with a placeholder with the text ‘Learn More’ and it’s value variable name is buttonText.

This means that developers don't have to make custom React components that hold other custom React components for each unique view or feature, rather, a uiSchema can be written to describe those steps.

Building Out the New Block Editors

Once we established this flow of generating uiSchemas by using uischema-builder and passing those instructions to JSF to render on our screen, it was time to figure out where in our existing codebase we wanted to build out the new block editors.

My team had a huge opportunity to create a brand new developer environment that gave us the freedom to introduce which tools we wanted to use to ensure that we didn’t slow down our overall development time.

The current landscape of product development at Squarespace is to work within a main, monolithic repo. When considering what kind of environment we wanted to work in while building out the new block editors, we considered placing our new code in the monolith. Ultimately, we decided against that and went down the path of creating our own repo because:

We wouldn’t be subject to a larger and more time-costly build and deploy pipeline

  • We would have complete control over the exact tests and checks we wanted to run without being affected by others code changes.

We would be able to publish frequent updates apart from the monolith which would speed up development

  • We would be able to build and publish our separate codebase as an npm package called @sqs/block-editor-schemas that is then consumed by the main monolith codebase. Having this be a separate build and publish step (outside of the monolith) makes it easier to rollout and rollback features.

  • This also means that we can better collaborate with Design and QA to address new features or known bugs.

We would be incentivized to prioritize quality commit messages, PRs, and guided documentation

  • A separate repo makes it easier to track general changes made across the project alongside creating more targeted and helpful documentation.

These reasons put the Core Layout team in a good position to now introduce incremental changes to the block editors that were currently rendered on the live platform.

With the help of our product manager and product designers, we created a block editor release roadmap that grouped block editors based on priority so that all team members knew which editors were going to be released in what order. Priority was based on frequency of use, so, for example, our button and image blocks were updated before our lesser used calendar or gallery blocks.

Since we had distinct block editor release groups, we were able to create feature flags responsible for rendering our new and legacy block editors within our QA and local development environments. This made it even easier for team members to know exactly which block editor was visible to whom.

When we started building the new block editors, my team was able to:

  1. Create the uiSchema for our most prioritized blocks using uischema-builder

  2. Build and publish a new version of the @sqs/block-editor-schemas npm package that includes the newly created block editor and any relevant updates

  3. Bump that package version in the monolithic repo

If the feature flag was turned on in development, the updates to the block editor would now be present only to developers who had the flag turned on to test locally.

This workflow allowed developers to validate their finalized changes quickly by simply bumping a package version in our monolith. Since all of these changes were behind feature flags, we were able to release these changes incrementally while our customers, and even most other Squarespace engineers, didn’t notice any difference in the block editing experience before any official release.

Rolling Out Features Without Affecting Our Customers

Since we’ve been able to quickly and consistently verify changes for the block editors with our development workflow, rolling out our first release group of blocks was as easy as turning on the feature flag for a portion of our customers in production and then slowly increasing the percentage of customers who would experience the new block editors.

For new release groups, we would start a staged rollout to about 10% of our customers, then 25%, then 50%, and finally 100%. Ideally, we plan on getting from 10% to 100% within a week, but we make sure that we increase the percentage primarily depending on how our customers are receiving the changes.

As of the publication of this article, we are still in the process of rolling out the rest of our new block editors.

Images of the updated block editors. On the left is the map block, and on the right is the video block.

Images of the updated block editors. On the left is the map block, and on the right is the video block.

With the help of our Customer Operations group, we monitor general block editor activity to make sure that our customers are able to complete the same actions they could with the legacy block editors. Depending on the severity of the customer-raised issue, Customer Operations notifies Core Layout, and we decide whether the bug can be fixed without rolling back or if the bug is detrimental to the overall block editing experience and should be reverted.

This workflow promotes a guarded approach to exposing technical enhancements to a highly visible portion of the Squarespace product. Our 40+ block editors make up a core feature that allows customers to create beautiful, custom sites. If the block editor experience is broken, then Squarespace wouldn’t be able to provide the core value that users sign up for.

Using this method, we can release highly-visible changes piece by piece with a level of flexibility that is required for customer satisfaction.

Encouraging Deleting Legacy Code

A great side effect to a project like reimagining our block editors is having the opportunity to identify and ultimately delete big portions of our legacy codebase. This is made especially possible with the use of block editor release groups since it’s easier for us to know which block editors can be deleted after a successful release.

Since this entire project focuses on creating a new and improved version of something that already exists on the platform, this is a prime opportunity for us to identify portions of the legacy codebase that can be deleted.

Conclusion

Because we wanted to stay consistent with our current design language, the Core Layout team has been able to establish a reliable development and release system that benefits not only our developers but also our designers and customers. The process that we’ve established has transformed a once seemingly boring, refactoring-technical-debt project into a project that enhances the overall block editing experience for customers, increases consistent and open collaboration between developers, QA, and designers, and encourages developers to use the latest frontend technologies.

As mentioned, this is a project that is ongoing and we have more block editor groups to release within the coming quarters. Fortunately, now that we have a tried and tested system that benefits everyone, rolling out these new changes to our customers is something we look forward to.

Collaborating to Build an Uncertain Layout

Collaborating to Build an Uncertain Layout

Synchronizing Application State Across Browser Frames

Synchronizing Application State Across Browser Frames