> This is part 1 of 2 of the full documentation (pages 1–100 of 153).
> The content is paginated: fetch every part to see all of it.
> Next part: https://docs.webstudio.is/llms-full.txt/1
> Page index: https://docs.webstudio.is/llms.txt

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
This site has an agentic ask interface you may use to query the documentation dynamically by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs.webstudio.is/index.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

---

# Welcome

New to Webstudio?

Our getting started guide will help you navigate through building your first Webstudio website. It covers the essentials of creating web designs and integrating third-party tools. You'll learn how to utilize Webstudio's unique features like Design tokens and Components, and how to contribute to the project.

[**Get started**](/basics/building-your-first-site)

***

{% content-ref url="/pages/DSnM361MrQuNrZ7bTZeZ" %}
[Building your first site](/basics/building-your-first-site)
{% endcontent-ref %}

{% content-ref url="/pages/SfKu4dh5vMVhYKIXk2hN" %}
[Foundations](/university/foundations)
{% endcontent-ref %}

{% content-ref url="/pages/FB2rVGXCaiQmkeNhPk48" %}
[Core Components](/university/core-components)
{% endcontent-ref %}

{% content-ref url="/pages/MGbnT1ttJc5oaRzOij1H" %}
[CLI](/university/cli)
{% endcontent-ref %}

{% content-ref url="/pages/sGAlO4KEzkyukQJleP83" %}
[Inception](/university/inception)
{% endcontent-ref %}

{% content-ref url="/pages/uQirhP7Wx6TZdVPdCcPA" %}
[Radix UI Components](/university/radix)
{% endcontent-ref %}

{% content-ref url="/pages/tQrdfGrQBblL34KdHOwX" %}
[Marketplace](/university/marketplace)
{% endcontent-ref %}

{% content-ref url="/pages/dCgydekXrUUDkt0F6JFD" %}
[Self-Hosting](/university/self-hosting)
{% endcontent-ref %}

{% content-ref url="/pages/bDIsvM8oJ65CIk3moc4h" %}
[How-Tos](/university/how-tos)
{% endcontent-ref %}

{% content-ref url="/pages/QBO45dANZ7aAa2PvblYx" %}
[Integrations](/university/integrations)
{% endcontent-ref %}

{% content-ref url="/pages/rTijqEDpmcrCmmGwL8U8" %}
[Craft](/university/craft)
{% endcontent-ref %}

{% content-ref url="/pages/tw1M9XIBhalXZrLNN3zp" %}
[FAQs](/basics/faq)
{% endcontent-ref %}

## Looking to contribute to Webstudio?

{% content-ref url="/pages/KZYMDX8bhjifbYsOh1YB" %}
[Contributions](/contributing/contributions)
{% endcontent-ref %}

{% content-ref url="/pages/AKSwRfjXlEHM0UrTs5fj" %}
[Contributing to the Marketplace](/contributing/marketplace)
{% endcontent-ref %}


# Intro

{% embed url="<https://vimeo.com/831343124>" %}

Webstudio is an open-source visual development platform that empowers designers and developers to create responsive web designs with ease. Bridging the gap between design and code, it allows users to visually build while maintaining control over the underlying HTML, CSS, and JavaScript.

## 🧑‍🎨 For Designers

Webstudio offers a powerful platform for designers to bring their creative visions to life, without needing extensive coding knowledge. Its intuitive visual interface enables the construction of layouts, styling of components, and animation of elements in real-time on a live canvas. This direct manipulation of design elements facilitates immediate feedback and accelerates iteration cycles.

## 🧑‍💻 For Developers

Developers can combine the visual editor with Resources, expressions, HTML Embed, and Webstudio MCP. Projects can be exported, self-hosted, or extended by contributing to the open-source Builder. Webstudio does not currently provide a visual workflow for defining new reusable component types; use Slots for reusable project content and HTML Embed for custom code.

## Related

* [Webstudio Features](https://webstudio.is/features) – Explore all Webstudio features
* [Building your first site](/basics/building-your-first-site) – Step-by-step guide to create your first website
* [Courses](/basics/courses) – Community-made courses for learning Webstudio
* [Anatomy of the Webstudio builder](/university/foundations/anatomy-of-the-webstudio-builder) – Understand the builder interface
* [Inception](/university/inception) – Explore Webstudio's standalone AI design exploration app
* [FAQs](/basics/faq) – Common questions and answers about Webstudio


# Building your first site

{% embed url="<https://www.youtube.com/playlist?list=PL4vVqpngzeT4Bfs_D25xNi_qNMY99R928>" %}
Webstudio 101 Playlist
{% endembed %}

This video series is a quick overview of the most important parts of the builder to get you up to speed.

## 🚀 Sign up for Webstudio

1. Visit [Webstudio](https://webstudio.is/) and click on the "start building" or "sign in" button.
2. Create an account using either Google or Github.

## 🌟 Create a new Project or import a template

Once logged in, you'll be taken to your dashboard. Here, you can view your existing projects and have the option to create a new blank project or start with a premade template.

## 🖥 Understand the Builder interface

Understanding the interface is key to effectively using any new tool. Familiarize yourself with Webstudio's builder – the heart of your website creation. The Components panel houses elements like text, images, links, and more, which you can drag onto your canvas. For an in-depth guide, read our [Intro to the Anatomy of the Webstudio builder](/university/foundations/anatomy-of-the-webstudio-builder).

## 🎨 Use design tokens

Webstudio utilizes Design tokens for styling, offering consistency across platforms. These reusable styles help avoid naming conflicts and dependency issues common in large projects. Learn more about [Design tokens in Webstudio](/university/foundations/design-tokens).

## 🔧 Experiment with components

Components are the building blocks of your website. Experiment with basic ones like Text and Image, or delve into advanced components like HTML Embeds or Radix. Customize each component’s properties under "Settings."

## 🌐 Preview and publish

Once you're satisfied with your creation, use the 'Preview' button at the top right corner before publishing it live on the internet! For more details, check our [Publishing and custom domains Guide](/university/foundations/publishing-and-custom-domains).

## 💬 Join the community

Learning a new tool takes time, so don't be discouraged by initial challenges. Keep exploring, experimenting, and [join the Webstudio community](https://wstd.us/community) for support and inspiration.

## Related

* [Courses](/basics/courses) – In-depth community-made courses
* [Intro](/basics/intro) – Overview of Webstudio for designers and developers
* [Anatomy of the Webstudio builder](/university/foundations/anatomy-of-the-webstudio-builder) – Deep dive into the builder interface
* [Core Components](/university/core-components) – Learn about all available components
* [Craft](/university/craft) – The standard for building with Webstudio


# Courses

Community-made courses

## [Webstudio Essentials: 0 to Complete Website With SEO](https://shop.createtoday.io/l/webstudio-essentials)

<figure><img src="https://330243581-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTep4EGj6hSC54ozHujyK%2Fuploads%2Fgit-blob-1b4b5d535b73225ed00c904087fc7dc520c31d84%2Fwebstudio-essentials-course.png?alt=media" alt="Webstudio Essentials course page"><figcaption></figcaption></figure>

## Related

* [Building your first site](/basics/building-your-first-site) – Quick video series to get started
* [Intro](/basics/intro) – Overview of Webstudio for designers and developers
* [Anatomy of the Webstudio builder](/university/foundations/anatomy-of-the-webstudio-builder) – Understand the builder interface
* [FAQs](/basics/faq) – Common questions and answers about Webstudio


# Roadmap & Links

## What is the team working on?

Despite being a small team, we're incredibly agile and committed to delivering a steady stream of fresh and exciting features to our users. We operate on a fortnightly release cycle for substantial feature updates, ensuring that Webstudio is always evolving and improving.

Transparency is one of our core values, and we believe in sharing our development journey with our community. Follow along with the design, development, marketing, and operations behind the scenes at Webstudio.

{% embed url="<https://wstd.us/roadmap>" %}
Roadmap at <https://wstd.us/roadmap>
{% endembed %}

Here you will find real-time updates and can see exactly what our team is working on, how far we've come, and what's coming up next. This open-door approach is part of our commitment to building a product that is as much a vision of our community as it is of our team.

### **Other Important Links:**

* **Website:** [Webstudio](https://webstudio.is/)
* **Discord:** [Join our community](https://wstd.us/community)
* **GitHub:** [Webstudio repository](https://github.com/webstudio-is/webstudio)
* **Product Hunt:** [Webstudio on Product Hunt](https://www.producthunt.com/products/webstudio)

### Social Media

* **YouTube:** [Subscribe on YouTube](https://www.youtube.com/@getwebstudio?sub_confirmation=1)
* **Twitter:** [Follow us on Twitter](https://twitter.com/getwebstudio)
* **LinkedIn:** [Connect on LinkedIn](https://www.linkedin.com/company/webstudio-inc/)

## Related

* [Webstudio Blog](https://webstudio.is/blog) – Read the latest news and tutorials
* [Intro](/basics/intro) – Learn what Webstudio is and who it's for
* [FAQs](/basics/faq) – Common questions and answers about Webstudio
* [Contributions](/contributing/contributions) – Contribute to Webstudio as a designer or developer


# FAQs

<details>

<summary>🌐 What is open source?</summary>

Open source software stems from the free software movement, emphasizing freedom in software usage, modification, and distribution. While terms like *open source*, *free*, *libre*, and *open core* have subtle differences, they share many principles. Key differentiators include licensing terms and the level of permissiveness.

The Open Source Initiative defines criteria for open source software, including free redistribution, access to source code, and allowance for modified/derived works. Licenses must not discriminate against any groups or use cases and must permit bundling with other software.

Some products blend open source and proprietary components, leading to the "open core" label, where the core functionality is open source but excludes proprietary parts. A well-known example is Android's use on Pixel phones: powered by the open source Android Open Source Project (AOSP), yet the phone's software remains proprietary.

</details>

<details>

<summary>🔑 How is Webstudio licensed?</summary>

Webstudio Builder is distributed under the AGPL license. The AGPL (Affero General Public License) is a copyleft license that requires any modified versions of the software to be released under the same license when it is used to provide services. Usage without modifications is free.

</details>

<details>

<summary>🔓 Is Webstudio Builder open source?</summary>

Webstudio Builder is open source, while the overall Webstudio Platform adopts an Open Core model.

[Open Source Definition](https://opensource.org/osd), [AGPL License](https://www.gnu.org/licenses/agpl-3.0.en.html)

</details>

<details>

<summary>💾 Can I use the CMS Integration when self-hosting?</summary>

**Yes**. [CMS](/university/foundations/cms) and [Resources](/university/foundations/cms#resources) are available when self-hosting the builder and exporting the projects. The speed at which remote data is fetched will depend on your hosting.

</details>

<details>

<summary>🌍 What is Cloudflare Workers edge deployment, and how does it work?</summary>

Cloudflare Workers are like small programs that run on Cloudflare's global network of servers. They allow you to customize and control how your website or application behaves, all without needing to manage your own server infrastructure. This global and distributed nature means that your code executes closer to your users, resulting in faster response times and improved reliability, regardless of where your users are located around the world. Plus, you can easily scale your application without worrying about provisioning or managing servers in different regions.

[Cloudflare Workers](https://workers.cloudflare.com/), [Cloudflare's open source workerd technology](https://blog.cloudflare.com/workerd-open-source-workers-runtime/)

</details>

<details>

<summary>🎨 What are design tokens?</summary>

Design tokens provide a unified system for managing styles like colors, spacing, and font sizes. They serve as a single source of truth for both designers and developers, housed in accessible formats like JSON files. This system ensures consistency across a project and simplifies design maintenance.

A common application of design tokens is in semantic color naming, where names correspond to usage rather than color values. The flexibility of design tokens allows for referencing, condition-specific alterations, and mathematical manipulations.

[WC3 Design tokens Format](https://tr.designtokens.org/format/), [Tokens Studio Plugin documentation](https://docs.tokens.studio/)

</details>

<details>

<summary>🛡️ What is GDPR, and how does it protect data? (What counts as GDPR compliant?)</summary>

The GDPR is a stringent data privacy law applicable globally to organizations handling EU citizens' data. It grants EU citizens rights such as access, rectification, erasure, and objection to automated decision-making. Organizations must adhere to principles like lawfulness, fairness, transparency, and accountability. Violations can result in significant fines.

Webstudio ensures its compliance with GDPR, although users must ensure their added functionalities on Webstudio sites also comply.

[What is GDPR?](https://gdpr.eu/what-is-gdpr/), [Who must comply with GDPR](https://gdpr.eu/companies-outside-of-europe/)

</details>

<details>

<summary>🖼️ How does Webstudio optimize images?</summary>

Images significantly impact web page sizes and loading times. Google's WebP format, supporting both lossless and lossy compression, alpha channels, and animation, helps reduce image sizes by about 30% compared to traditional formats.

Webstudio automatically converts images to WebP and resizes them to fit webpage dimensions, ensuring optimal sizes without compromising quality.

[WebP FAQ](https://developers.google.com/speed/webp/faq)

</details>

<details>

<summary>🚀 What can I build with Webstudio today?</summary>

Webstudio builds responsive static and dynamic websites with visual CSS controls, animations, data Resources, Content mode, the Content Engine, and accessible components. You can work in the visual Builder or let an AI agent edit the same project through Webstudio MCP. Projects can be published to Webstudio Cloud or exported for self-hosting.

[Webstudio features](https://webstudio.is/features), [Webstudio roadmap](https://wstd.us/roadmap)

</details>

<details>

<summary>🆘 How to get additional support for my Webstudio purchase?</summary>

Please [read this article](/misc/webstudio-support-process) on where and how to get support.

</details>

## Related

* [Webstudio FAQ](https://webstudio.is/faq) – More frequently asked questions
* [Webstudio Experts](https://webstudio.is/experts) – Find certified Webstudio experts for your project
* [Intro](/basics/intro) – Learn what Webstudio is and who it's for
* [Courses](/basics/courses) – Community-made courses for learning Webstudio
* [Roadmap & Links](/basics/roadmap-and-links) – Official links and what the team is working on
* [Webstudio Support Process](/misc/webstudio-support-process) – How to get help with your account
* [Account Limits](/misc/account-limits) – View limits for different plan types


# Foundations

{% content-ref url="/pages/gWk5LWLMojqZypn1DiPC" %}
[Anatomy of the Webstudio builder](/university/foundations/anatomy-of-the-webstudio-builder)
{% endcontent-ref %}

{% content-ref url="/pages/uYZ35ePvw8AH9gDOcQXO" %}
[CSS variables](/university/foundations/css-variables)
{% endcontent-ref %}

{% content-ref url="/pages/bZ5bEQZSTGdtoAnBB8DA" %}
[Design tokens](/university/foundations/design-tokens)
{% endcontent-ref %}

{% content-ref url="/pages/zrAAfdIUeGKRarVi8Ulx" %}
[Variables](/university/foundations/variables)
{% endcontent-ref %}

{% content-ref url="/pages/rOfjNC98NZMAsf6wSZzS" %}
[Expression editor](/university/foundations/expression-editor)
{% endcontent-ref %}

{% content-ref url="/pages/11SC2sKf8QtsO6d5BRt8" %}
[Animations](/university/foundations/animations)
{% endcontent-ref %}

{% content-ref url="/pages/NWE7BTYGPm7c6owvJAJf" %}
[CMS](/university/foundations/cms)
{% endcontent-ref %}

{% content-ref url="/pages/2tP4arpLdkZdYCxzk0Pq" %}
[Copy-Paste](/university/foundations/copy-paste)
{% endcontent-ref %}

{% content-ref url="/pages/wz3tgGLzRzYbcpeG6cHy" %}
[Commands & search](/university/foundations/commands-and-search)
{% endcontent-ref %}

{% content-ref url="/pages/IXTuflumgHpMapfKa1ZT" %}
[SEO settings](/university/foundations/seo-settings)
{% endcontent-ref %}

{% content-ref url="/pages/uqSIHA6qTMB3tdBV8Tgb" %}
[Shortcuts](/university/foundations/shortcuts)
{% endcontent-ref %}

{% content-ref url="/pages/Fle5lPeggLTWPlu0RYPf" %}
[Project settings](/university/foundations/project-settings)
{% endcontent-ref %}

{% content-ref url="/pages/o7wRtKAe7k78J7XeVdD7" %}
[Modes](/university/foundations/modes)
{% endcontent-ref %}

{% content-ref url="/pages/kqo6OJJGoFd6tYSEi7lI" %}
[Share links](/university/foundations/share-links)
{% endcontent-ref %}

{% content-ref url="/pages/HO5k2SucEYS6ecG6TUlw" %}
[Publishing & custom domains](/university/foundations/publishing-and-custom-domains)
{% endcontent-ref %}


# Dashboard

The Dashboard is your central hub for managing all your Webstudio projects, featuring workspaces, search, organization with tags, and multiple view options.

<figure><img src="https://330243581-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTep4EGj6hSC54ozHujyK%2Fuploads%2Fgit-blob-b1f457513bf93e116f1162fc509bca4b88caf1dd%2Fdashboard.webp?alt=media" alt="Webstudio dashboard showing project cards"><figcaption><p>The Webstudio Dashboard</p></figcaption></figure>

The Dashboard is the first screen you see after logging into Webstudio. It provides an overview of your workspaces and projects, with powerful features for organization and quick access.

## Interface theme

Open the profile menu and choose **Theme**, then select **System**, **Light**, or **Dark**. **System** follows your operating system setting and updates when that setting changes. The preference applies to both the Dashboard and the Builder.

<picture><source srcset="/files/hdnmrG2t4C8SgrKQX33S" media="(prefers-color-scheme: dark)"><img src="https://330243581-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTep4EGj6hSC54ozHujyK%2Fuploads%2Fgit-blob-492388633de7145143fa1bdf9e90c2a2272600f9%2Fdashboard-theme-light.png?alt=media" alt="Dashboard profile menu with the Theme submenu open and System, Light, and Dark options visible"></picture>

In the Builder, the same preference is available from **Menu > View > Theme**. It changes the Webstudio interface, not the site on the canvas. To design and preview a site color scheme, use a [color-scheme breakpoint](/university/foundations/breakpoints#example-dark-mode).

***

## Workspaces

Workspaces let you organize projects and collaborate with other people. Each workspace has its own projects, members, roles, and seats.

Use workspaces when you want to:

* Keep client, team, or personal projects separate
* Give collaborators access to multiple projects at once
* Control what each person can view, edit, build, or publish
* Move projects between workspaces you can manage

### Creating a workspace

1. Open the workspace selector in the Dashboard sidebar
2. Choose "New workspace"
3. Enter a workspace name
4. Click "Create"

Workspace creation is limited by your plan.

<figure><img src="https://330243581-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTep4EGj6hSC54ozHujyK%2Fuploads%2Fgit-blob-4424e9931e254e42bfd215aba2fa51e6aba391f2%2Fworkspace-selector.png?alt=media" alt="Workspace selector open in the Dashboard sidebar"><figcaption><p>Workspace selector</p></figcaption></figure>

<figure><img src="https://330243581-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTep4EGj6hSC54ozHujyK%2Fuploads%2Fgit-blob-c2a7c8051e4a9bfc27d8147dc677a1d41317b0df%2Fnew-workspace-dialog.png?alt=media" alt="New workspace dialog with a workspace name field"><figcaption><p>Creating a new workspace</p></figcaption></figure>

### Switching workspaces

Use the workspace selector in the Dashboard sidebar to switch between workspaces. The project list, search results, and tags update to match the selected workspace.

### Members and roles

Workspace owners can manage members from the workspace menu. Add members by entering their email addresses; Webstudio sends them a secure dashboard notification to accept. Multiple emails can be entered at once by separating them with commas.

Unlike share links, workspace membership is tied to a user's Webstudio account. This makes membership invitations safer for ongoing collaboration, especially when access should not depend on whether someone keeps a link private.

<figure><img src="https://330243581-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTep4EGj6hSC54ozHujyK%2Fuploads%2Fgit-blob-98ab88ca2006846ba77e19c64cfcb03cb71c73c8%2Fworkspace-members-dialog.png?alt=media" alt="Workspace Members dialog showing invite field, role selector, member list, and included seats"><figcaption><p>Managing workspace members</p></figcaption></figure>

When inviting or updating a member, choose a role:

| Role    | What they can do                                                    |
| ------- | ------------------------------------------------------------------- |
| Viewer  | View, copy instances, and clone projects.                           |
| Editor  | Edit content only, such as text, images, and predefined components. |
| Builder | Make design changes and publish to staging.                         |
| Admin   | Make design changes and publish to custom domains.                  |

Owners have full control of the workspace, including member management and billing-related actions. Owners cannot be removed from their own workspace.

<figure><img src="https://330243581-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTep4EGj6hSC54ozHujyK%2Fuploads%2Fgit-blob-32daa05f194d74cbae103133dd9d54ee5c430a4d%2Fworkspace-role-selector.png?alt=media" alt="Workspace role selector open with Viewer, Editor, Builder, and Admin options"><figcaption><p>Workspace roles</p></figcaption></figure>

### Pending invites

Invited members appear as pending until they accept the invitation. Workspace owners can remove pending invites from the Members dialog.

### Seats and billing

Each workspace has an included seat count based on the workspace owner's plan. The Members dialog shows how many seats are still included.

If an invite would exceed the included seats, Webstudio asks you to confirm the extra seats before sending the invite. Extra seats are added to billing for the workspace owner.

If a workspace has more members than the plan covers, non-owner members cannot access the workspace until the owner buys the extra seats or removes members.

### Moving and transferring projects

From a project menu, choose "Transfer" to move or transfer a project:

* Move it to another workspace you can manage
* Transfer it to another user's workspace that you already have access to
* Transfer it to another user by entering their email address

If the recipient has no shared workspace available, Webstudio sends a transfer request. When the recipient accepts it, the project is placed in their default workspace.

<figure><img src="https://330243581-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTep4EGj6hSC54ozHujyK%2Fuploads%2Fgit-blob-729145d3f42548eaa8cfbdd8ed6a65164dc0a399%2Fproject-transfer-dialog.png?alt=media" alt="Project transfer dialog with workspace selector and recipient email field"><figcaption><p>Moving or transferring a project</p></figcaption></figure>

{% hint style="info" %}
Workspaces are best for secure ongoing collaboration. Share links are still useful for one-off access, support, marketplace templates, and transferring cloneable copies, but anyone with the link can use it according to its permissions. See [Share links](/university/foundations/share-links).
{% endhint %}

## Search

The Dashboard includes search to help you quickly find projects in the selected workspace.

### How to use search

1. Click the search field in the left sidebar or start typing directly
2. Type your search query to filter projects by name
3. Use arrow keys to navigate through search results
4. Press Enter to open the selected project

Search displays matching projects in a dedicated search view. This allows you to find any project in the selected workspace regardless of which section you're currently viewing.

{% hint style="info" %}
Search is performed client-side for instant results, making it snappy even with many projects.
{% endhint %}

***

## Project tags

Tags help you organize and categorize your projects for easier management. This is especially useful when you have many projects and need to group them by client, project type, status, or any other criteria.

### Creating and managing tags

1. Hover over a project card and click the tag icon (or access via the project menu)
2. Click "Create new tag" to add a new tag
3. Enter a tag name and select a color
4. Tags are automatically saved when you select them

### Tag features

* **Color-coded tags** – Each tag can have a distinct color for visual organization
* **Multiple tags per project** – Assign as many tags as needed to a single project
* **Filter by tags** – Click a tag in the sidebar to filter projects by that tag
* **Edit tags** – Rename or change the color of existing tags
* **Delete tags** – Remove tags you no longer need

Tags appear on project cards, making it easy to see project categories at a glance.

***

## View options

The Dashboard supports two different view layouts to suit your preference:

### Grid view (card view)

The default view displays projects as visual cards with:

* Project thumbnail/preview
* Project name
* Custom domain (if configured)
* Assigned tags
* Quick action buttons

### List view

A compact view that displays projects in rows with:

* Project thumbnail (smaller)
* Project name
* Domain information
* Tags
* Last published date
* Created date

Switch between views using the view toggle buttons in the toolbar.

***

## Sorting projects

Projects can be sorted in multiple ways to help you find what you need:

* **Last published** – Projects sorted by most recent publication date
* **Last edited** – Projects sorted by most recent changes (default)
* **Name (A-Z)** – Alphabetically ascending
* **Name (Z-A)** – Alphabetically descending
* **Date created** – Projects sorted by creation date

Click the sort dropdown in the toolbar to change the sorting order. Your preference is remembered across sessions.

***

## Custom domain display

When a project has a custom domain configured, the domain is displayed on the project card. This makes it easy to identify which projects are live and what their public URLs are without opening each project.

The domain display shows:

* Custom domain (e.g., `example.com`) if configured
* Default Webstudio subdomain otherwise

***

## Project settings from Dashboard

You can access project settings directly from the Dashboard without opening the Builder:

1. Click the menu icon (three dots) on a project card
2. Select "Settings" from the dropdown menu
3. Edit project settings in the dialog that appears

This allows you to quickly update project metadata, configure redirects, manage custom code, and adjust other settings without loading the full Builder interface.

***

## Project actions

From the Dashboard, you can perform several actions on your projects:

### Quick actions (hover over card)

* **Open** – Open the project in the Builder
* **Tags** – Manage project tags
* **Menu** – Access more options

### Menu actions

* **Settings** – Open project settings dialog
* **Duplicate** – Create a copy of the project
* **Share** – Create a share link
* **Transfer** – Move the project to another workspace or transfer it to another user
* **Delete** – Remove the project (with confirmation)

Available actions depend on your workspace role. For example, Editors can rename and tag projects, Builders can create and duplicate projects, and Admins can transfer projects.

## Related

* [Project settings](/university/foundations/project-settings) – Configure project-wide settings
* [Publishing & custom domains](/university/foundations/publishing-and-custom-domains) – Deploy your site and manage domains
* [Share links](/university/foundations/share-links) – Create share links and cloneable transfers
* [Anatomy of the Webstudio builder](/university/foundations/anatomy-of-the-webstudio-builder) – Learn the Builder interface


# Anatomy of the Webstudio builder

This guide discusses the anatomy of the Webstudio Builder, an all-in-one visual development platform that allows you to build advanced websites.

{% embed url="<https://www.youtube.com/playlist?list=PL4vVqpngzeT4Bfs_D25xNi_qNMY99R928>" %}
Webstudio 101 Playlist
{% endembed %}

<picture><source srcset="/files/anvQNtJ4qL85lPhfkby9" media="(prefers-color-scheme: dark)"><img src="https://330243581-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTep4EGj6hSC54ozHujyK%2Fuploads%2Fgit-blob-0ab9c7fe88adadf878ae2d45501172113ec57a9e%2Fbuilder-overview-light.png?alt=media" alt="Webstudio Builder with the Navigator on the left, canvas in the center, and Style panel on the right"></picture>

## Canvas

The canvas provides a visual representation of the website you are creating. After adding components from the Components panel, you can arrange and style their instances on the canvas.

## Navigator

The Navigator Panel is a hierarchical overview of all instances on your page. It displays the website’s structure, showing the nesting and relationships between different instances. You can select the instance of any component inside your Project by clicking it on the canvas or inside the navigator.

### Rename instances

Double-click any instance in the Navigator to rename it. Use semantic names like "Hero Section" or "Contact Form" to make your project structure easier to understand and maintain. In the bottom half of the navigator, you will find the CSS Preview section, which is a real-time preview of the CSS styles applied to your selected instance.

<picture><source srcset="/files/YzsTKXTd8nnTmYnHVHno" media="(prefers-color-scheme: dark)"><img src="https://330243581-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTep4EGj6hSC54ozHujyK%2Fuploads%2Fgit-blob-47b20f3aa5925c1fac67074b5e3f9f940c0241d3%2Fnavigator-panel-light.png?alt=media" alt="Navigator panel showing Global root, Body, and a nested Content Block instance"></picture>

### Global root

In the Navigator is the Global Root, the highest level of the page. Changes made to it apply to every page. It’s useful for setting global styles, such as font size and line height, and defining [CSS variables](/university/foundations/css-variables) so they are accessible on every instance on every page. Changing the font size on the root affects all CSS properties that use REM, as this unit is relative to the root font size. For example, `1rem` outputs as `1 x root font size`.

{% hint style="info" %}
Global Root uses the`:root` [CSS selector](https://developer.mozilla.org/en-US/docs/Web/CSS/:root) under the hood.
{% endhint %}

<picture><source srcset="/files/YzsTKXTd8nnTmYnHVHno" media="(prefers-color-scheme: dark)"><img src="https://330243581-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTep4EGj6hSC54ozHujyK%2Fuploads%2Fgit-blob-47b20f3aa5925c1fac67074b5e3f9f940c0241d3%2Fnavigator-panel-light.png?alt=media" alt="Navigator with Global root above Body and the page instance tree"></picture>

## Breakpoints

Breakpoints are crucial for creating responsive websites that adapt to different screen sizes and devices. *Style* changes you make on one breakpoint cascade, or affect, that breakpoint and all the smaller ones.

{% hint style="success" %}
You can create custom breakpoints with any media query condition – not just width-based. This includes `prefers-color-scheme` for dark mode, `prefers-reduced-motion` for accessibility, `orientation`, and more.
{% endhint %}

{% hint style="warning" %}
Adding too many breakpoints or mixing and matching `min-width` and `max-width` will make maintenance difficult. The default breakpoints suffice more in the majority of use cases.
{% endhint %}

<picture><source srcset="/files/QXPCFxkUfmD2q4xGOBFy" media="(prefers-color-scheme: dark)"><img src="https://330243581-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTep4EGj6hSC54ozHujyK%2Fuploads%2Fgit-blob-bc1637b89fbd9a481c7cea1f9f24e2100131065d%2Fbreakpoints-light.png?alt=media" alt="Breakpoint menu listing desktop, tablet, and mobile breakpoint conditions"></picture>

By defining how components should behave at different screen sizes, you can ensure your website looks great on various devices, including desktops, tablets, and smartphones.

{% hint style="info" %}
When you select a breakpoint, such as 991, you’ll notice that the canvas is sized to 768. This is intentional. The goal is to style for the minimum (or maximum, if using min-width) to ensure all design issues are addressed at the "extreme" end of that breakpoint. When the viewport changes to 767, the next breakpoint is triggered.
{% endhint %}

## Components panel

The Components Panel contains a list of all available [components](/university/core-components) that you can add to your Webstudio Project. You can do this by clicking the components in the panel or dragging and dropping them on the canvas.

Components are grouped into sections like General, Text, Media, Forms, and [Radix](/university/radix). For example, the Text section has all typography-related components, while the Form section nests the building blocks of a form.

<picture><source srcset="/files/GW27fUw54Kz1uYwqQkZZ" media="(prefers-color-scheme: dark)"><img src="https://330243581-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTep4EGj6hSC54ozHujyK%2Fuploads%2Fgit-blob-d83bd1cac27b29a5fd037b3f8d6a27521380d365%2Fcomponents-panel-light.png?alt=media" alt="Components panel with a search field and expanded General, Typography, and Media groups"></picture>

## Assets panel

The Assets Panel is the second panel to the left of your canvas, and this is where all the static files are stored. You can upload, organize, and manage project assets inside this panel before using them on the canvas.

<picture><source srcset="/files/8HXShDzjvvPraIzN8Zeu" media="(prefers-color-scheme: dark)"><img src="https://330243581-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTep4EGj6hSC54ozHujyK%2Fuploads%2Fgit-blob-7be4b45d79ffa250bf8f12d0a4e59eef0c2fc6bc%2Fassets-panel-light.png?alt=media" alt="Assets panel with search, filter and sort controls above folders and files"></picture>

### Asset types

The Assets Panel supports various file types organized by category:

* **Images** – JPEG, PNG, GIF, WebP, SVG, ICO, and more
* **Fonts** – WOFF, WOFF2, TTF, OTF, and more
* **Documents** – PDF, JSON, XML, and more
* **Other** – Various additional file formats

This list is not exhaustive – Webstudio supports many additional file formats.

### Filter and sort

Use the filter dropdown to show only specific asset categories (Images, Fonts, Documents, or All). Assets can be sorted by:

* **Name** – Alphabetically A-Z or Z-A
* **Date** – Newest or oldest first
* **Size** – Largest or smallest first

### Asset details

Click on any asset to view its details panel, which shows:

* **Name** – Click to rename the asset
* **Description** – Add optional description text for organization
* **Dimensions** – Width and height for images
* **File size** – Size of the asset file
* **Uses** – Number of places the asset is used in your project

### Asset actions

* **Download** – Download the original asset file to your computer (Pro plan required)
* **Review & Delete** – When an asset is in use, shows where it's used before confirming deletion. Unused assets can be deleted directly.
* **Delete unused assets** – Click the brush icon in the Assets Panel header to review unused assets. All assets are selected by default; uncheck any you want to keep or use **Select all** to change the complete selection. Confirming deletes only selected assets. This command is also available from the [Command Panel](/university/foundations/commands-and-search).

{% hint style="info" %}
Assets that are currently used in your project will show a settings gear icon. Attempting to delete them will display a list of all usages so you can review before confirming.
{% endhint %}

## Pages panel

The Pages Panel offers an overview of the website’s page structure and hierarchy, and it is the last panel on the left side of your canvas, under the Components and Assets Panels.

You can use this panel to add new pages to your Webstudio site, set the homepage, rename existing ones, and configure fields for social sharing and search engines.

<picture><source srcset="/files/BO7vrAZIC2xluz3RIxpl" media="(prefers-color-scheme: dark)"><img src="https://330243581-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTep4EGj6hSC54ozHujyK%2Fuploads%2Fgit-blob-3e1aeef2aae551e4a760840eb29f1ae10680e66c%2Fpages-panel-light.png?alt=media" alt="Pages panel with Home and an expanded Blog folder containing Post, Author, and Posts pages"></picture>

### Page folders

Organize your pages into collapsible folders for better project management. Right-click in the Pages Panel to create a new folder, then drag pages into it. Folders help keep large projects organized and make navigation easier.

## Style panel

The Style Panel is located to the right of the canvas, and you can use it to customize the appearance and layout of a selected instance. It offers access to all CSS properties, visually.

<picture><source srcset="/files/cfHDvv3Mmbzjh5Rxb9Lk" media="(prefers-color-scheme: dark)"><img src="https://330243581-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTep4EGj6hSC54ozHujyK%2Fuploads%2Fgit-blob-25a7e051ecdd904edd1f0797f38f0e5c02febec9%2Fstyle-panel-light.png?alt=media" alt="Style panel showing style sources, layout, grid child, spacing, and size controls"></picture>

There are three methods for adding styles:

1. Local - By default, the Local icon is active, meaning any styles you apply to that instance are for that instance only. The dot in the middle of the Local icon indicates that it has styles, whereas no dot indicates that there are no styles applied, making it easy to identify which ones have styles applied.
2. [CSS variables](/university/foundations/css-variables) - Instead of pasting in your colors, sizes, and other styles, you can create a variable for each style and access the variables in each input field. For example, you can define a variable called "color-primary," and in your border color field, you can enter the variable name instead of the color itself.
3. [Tokens](/university/foundations/design-tokens) - These enable reusing groupings of styles across your site. You can either create a new Token and apply styles to it or start with Local and convert it to a Token. Tokens are typically comprised of CSS variables and one-off styles.

### Label colors

The style input labels change colors indicating there is a style present.

| Label color                                                           | What it means                                                                                                                                                                                                       |
| --------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| <mark style="background-color:blue;">**Blue**</mark>                  | This style is on the current Token.                                                                                                                                                                                 |
| <mark style="color:orange;background-color:orange;">**Orange**</mark> | This style is coming from another source: a Token that is not selected, a state not selected (e.g., you're on hover but the style is local), inherited from a parent instance, or cascaded from another breakpoint. |
| <mark style="background-color:yellow;">**Gray**</mark>                | This is a Webstudio or browser default style (like font-family: arial).                                                                                                                                             |
| <mark style="color:red;background-color:red;">**Red**</mark>          | This style is on the current Style Source, but it’s being overwritten by something else in the Style Source input, such as Local or another Token.                                                                  |

{% hint style="info" %}
The order of tokens in the Style Source Input matters. When multiple Tokens have a value for the same property, Tokens toward the end of the list will overwrite Tokens toward the beginning of the list.
{% endhint %}

## Settings panel

The Settings Panel is on the right side of your canvas. You can use this panel to access and edit component-specific properties (such as ID or class) and [Variables](/university/foundations/variables) for a selected instance, enabling you to store reusable content and fetch APIs — a building block of [CMS](/university/foundations/cms).

***

## Modes

[Modes](/university/foundations/modes) change the Builder's behavior, such as previewing your site without distractions.

<picture><source srcset="/files/i2NwYvqykMgOH0PWbTRQ" media="(prefers-color-scheme: dark)"><img src="https://330243581-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTep4EGj6hSC54ozHujyK%2Fuploads%2Fgit-blob-57f4391cbd164552833d61dd6f20d3c6a5bb709f%2Fbuilder-modes-light.png?alt=media" alt="Mode menu with Design and Content options and their keyboard shortcuts"></picture>

## Interface theme

Choose **Menu > View > Theme**, then select **System**, **Light**, or **Dark**. **System** follows your operating system setting. The same preference is available from the [Dashboard](/university/foundations/dashboard#interface-theme) profile menu and applies to both interfaces.

The interface theme does not change the site on the canvas. To design and preview a site color scheme, use a [color-scheme breakpoint](/university/foundations/breakpoints#example-dark-mode).

## Hide UI

Hide UI gives the canvas the full builder workspace by hiding the sidebars, footer, and top bar. It works in any mode, so you can keep using Design, Content, or Preview while the Builder chrome is hidden.

Use Hide UI when you want more canvas space while designing, editing content, or previewing a page. Move the pointer to the top edge to temporarily reveal the top bar.

You can enable Hide UI from **Menu > View > Hide UI** or with `⌘ + \` on Mac and `Ctrl + \` on Windows.

<picture><source srcset="/files/CYJCGuLIOIFbSG69ML59" media="(prefers-color-scheme: dark)"><img src="https://330243581-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTep4EGj6hSC54ozHujyK%2Fuploads%2Fgit-blob-a6d052d74966c0a50e97ed0ea18247374954d0ef%2Fbuilder-hide-ui-menu-light.png?alt=media" alt="Builder menu with the View submenu open and Hide UI available with its keyboard shortcut"></picture>

## Share dialog

The [Share Dialog](/university/foundations/share-links) allows you to create shareable personal links to your Project with varying permissions.

***

## Publish dialog

The [Publish Dialog](/university/foundations/publishing-and-custom-domains) enables you to add a custom domain, publish to Staging and/or your custom domain, and [export your Project](/university/self-hosting).

Publishing builds and deploys the current Project. Keep the Publish dialog open to follow its status and review any reported errors.

## Related

* [Shortcuts](/university/foundations/shortcuts) – Keyboard shortcuts to speed up building
* [Commands & search](/university/foundations/commands-and-search) – Navigate and perform actions quickly
* [Design tokens](/university/foundations/design-tokens) – Create reusable style packages
* [CSS variables](/university/foundations/css-variables) – Define and reuse style values
* [Modes](/university/foundations/modes) – Switch between Design, Content, and Preview modes


# Navigator

Use the Navigator to view, select, and organize instances on your page.

The Navigator is a hierarchical panel on the left side of the builder that shows every instance on the current page as a tree. It reflects the exact nesting structure of your page and is the primary way to select, reorder, and organize instances — especially when they are stacked or overlapping on the canvas.

<picture><source srcset="/files/YzsTKXTd8nnTmYnHVHno" media="(prefers-color-scheme: dark)"><img src="https://330243581-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTep4EGj6hSC54ozHujyK%2Fuploads%2Fgit-blob-47b20f3aa5925c1fac67074b5e3f9f940c0241d3%2Fnavigator-panel-light.png?alt=media" alt="Navigator panel showing Global root, Body, and a nested Content Block instance"></picture>

## Selecting instances

Click any item in the Navigator to select it. The selected instance is also highlighted on the canvas. Selecting from the Navigator is especially useful for:

* Instances that are invisible or have zero size on the canvas
* Instances hidden behind other instances
* Instances inside a collapsed container

### Select multiple instances

You can select multiple sibling instances and edit their shared settings in one operation:

* Hold `Command` on macOS or `Ctrl` on Windows and click to add or remove an instance from the selection.
* Hold `Shift` and click to select a range.
* Press `Shift + Up Arrow` or `Shift + Down Arrow` to extend the selection.
* Press `Command + A` on macOS or `Ctrl + A` on Windows to select all siblings of the current instance.

Copy, cut, duplicate, delete, and compatible style or settings changes apply to the selection. Webstudio skips instances that cannot accept an operation and keeps the remaining selection intact.

### Link to a selected instance

Right-click an instance and choose **Copy link to instance** to copy its full Builder URL to the clipboard. Share the link to open the same page and select the same instance, including its location inside a shared Slot. Select one instance at a time; this action is unavailable for Global Root.

The recipient needs access to the project. If you opened the Builder through a share link, the copied URL keeps that share link’s access token.

You can also copy the browser URL, which tracks the selected page and instance. Webstudio also uses these links for actions such as **Show element** in pre-publish findings. If the instance was removed before the link is opened, the Builder still opens the requested page.

You can give this link to an AI assistant connected to the project through [MCP](/university/mcp) to identify the instance you want to change.

## Renaming instances

Double-click any item in the Navigator to rename it. Use descriptive names like "Hero Section" or "Product Card" to make large projects easier to navigate.

## Reordering and nesting

Drag items in the Navigator to reorder or re-nest them. This is the most reliable way to restructure deeply nested layouts.

You can also move the selected instance or sibling selection with the keyboard:

* `Ctrl + Up Arrow` or `Ctrl + Down Arrow` moves it before or after a sibling.
* `Ctrl + Left Arrow` moves it out of its parent.
* `Ctrl + Right Arrow` moves it into the previous sibling.

## Show / hide

Right-click any item in the Navigator to toggle its visibility on the canvas. Hidden instances are still rendered in the published site unless you use a [display condition](/university/foundations/expression-editor#binding) or set `display: none` in the Style Panel.

## Global Root

At the top of the Navigator is the **Global Root** — the highest level in the instance tree. Styles set here apply to every page in the project. See [Anatomy of the builder](/university/foundations/anatomy-of-the-webstudio-builder#global-root) for a full explanation.

## CSS preview

Below the instance tree, the Navigator shows a **CSS Preview** — a read-only view of the computed CSS styles applied to the currently selected instance. It is useful for quickly checking what styles are active without opening the Style Panel.

## Related

* [Anatomy of the builder](/university/foundations/anatomy-of-the-webstudio-builder) – Overview of all builder panels
* [Style panel](/university/foundations/style-panel) – Apply styles to selected instances
* [CSS variables](/university/foundations/css-variables) – Define global variables on the root
* [Expression editor](/university/foundations/expression-editor) – Conditionally show or hide instances


# Layout & Flexbox

Learn how to create multi-column layouts and control element positioning using Flexbox in Webstudio.

Understanding layout is fundamental to building websites in Webstudio. This guide covers the display modes, Flexbox properties, and best practices for creating responsive layouts.

{% embed url="<https://www.youtube.com/watch?v=12cJHLhaBFk>" %}
Style Panel Overview & Flexbox Basics
{% endembed %}

***

## Display Modes

The **Display** property in the Layout section controls how an element and its children are positioned. The main display modes are:

| Display          | Description                                                     |
| ---------------- | --------------------------------------------------------------- |
| **Block**        | Default for boxes. Elements stack vertically, taking full width |
| **Flex**         | Enables flexible layouts with horizontal/vertical axis control  |
| **Grid**         | Two-dimensional layout system (rows and columns)                |
| **Inline**       | Elements flow inline like text                                  |
| **Inline Block** | Inline flow but respects width/height                           |
| **None**         | Hides the element completely                                    |

<figure><img src="https://330243581-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTep4EGj6hSC54ozHujyK%2Fuploads%2Fgit-blob-d9e9e75dc8692d29f6a3badf3978d6c5d85ea348%2Flayout-section.png?alt=media" alt="Layout section in the Style Panel showing display mode and flex controls"><figcaption><p>The Layout section in the Style Panel</p></figcaption></figure>

***

## Flexbox Fundamentals

Flexbox is the primary tool for creating layouts in Webstudio. When you change display to **Flex**, you gain control over how child elements are arranged.

### Enabling Flexbox

1. Select the **parent** container (the element that holds your items)
2. Go to the **Style Panel** → **Layout** section
3. Change **Display** from `Block` to `Flex`

### Flex Direction

Control whether children are arranged horizontally or vertically:

* **Row** (default): Items arranged horizontally left to right
* **Row Reverse**: Items arranged horizontally right to left
* **Column**: Items stacked vertically top to bottom
* **Column Reverse**: Items stacked vertically bottom to top

### Alignment

Flexbox provides two axes for alignment:

| Property            | Description                                                                |
| ------------------- | -------------------------------------------------------------------------- |
| **Justify Content** | Aligns items along the main axis (horizontal for row, vertical for column) |
| **Align Items**     | Aligns items along the cross axis                                          |

Common alignment values:

* **Start**: Items at the beginning
* **Center**: Items centered
* **End**: Items at the end
* **Space Between**: Equal space between items
* **Space Around**: Equal space around items
* **Space Evenly**: Equal space including edges

### Gap

Add consistent spacing between flex items using the **Gap** property. This is cleaner than adding margins to individual items.

### Flex Wrap

By default, flex items try to fit on one line. Enable **Flex Wrap** to allow items to wrap to the next line when they don't fit:

1. In the Layout section, find the **Wrap** toggle
2. Click to enable wrapping
3. Items will now flow to the next line when space runs out

{% hint style="info" %}
Flex wrap is essential for responsive card layouts. Combined with min-width on cards, they'll automatically stack on smaller screens.
{% endhint %}

***

## Common Layout Patterns

### Multi-Column Card Layout

{% embed url="<https://www.youtube.com/watch?v=oL42jVHlaqE>" %}
Building Your First Section
{% endembed %}

Structure:

```
Section (semantic tag)
└── Container (max-width, centered)
    └── Cards (flex parent)
        ├── Card
        ├── Card
        └── Card
```

Steps:

1. Create a **Box** and change tag to `section`
2. Add a **Box** inside as a container (for max-width)
3. Add another **Box** for the cards wrapper
4. Set the cards wrapper to `display: flex`
5. Add **Gap** between cards
6. Enable **Flex Wrap** for responsiveness
7. Add individual **Card** boxes inside

### Centering Content

To center a single element both horizontally and vertically:

1. Set parent to `display: flex`
2. Set **Justify Content** to `Center`
3. Set **Align Items** to `Center`

### Navigation Layout

For a typical header with logo and nav items:

1. Header container: `display: flex`
2. **Justify Content**: `Space Between` (logo left, nav right)
3. **Align Items**: `Center` (vertically centered)

***

## Sizing Properties

### Width & Height

| Property       | Description                        |
| -------------- | ---------------------------------- |
| **Width**      | Fixed width of element             |
| **Min Width**  | Minimum width (won't shrink below) |
| **Max Width**  | Maximum width (won't grow beyond)  |
| **Height**     | Fixed height of element            |
| **Min Height** | Minimum height                     |
| **Max Height** | Maximum height                     |

### Flex Child Properties

When an element is inside a flex container, additional properties become available:

| Property        | Description                                              |
| --------------- | -------------------------------------------------------- |
| **Flex Grow**   | How much the item should grow relative to siblings       |
| **Flex Shrink** | How much the item should shrink relative to siblings     |
| **Flex Basis**  | Initial size before growing/shrinking                    |
| **Align Self**  | Override the parent's align-items for this specific item |

***

## Container Best Practices

### Section + Container Pattern

A common pattern for page sections:

1. **Outer Box** (Section tag)
   * Full width background colors
   * Vertical padding for spacing
2. **Inner Box** (Container)
   * `max-width: 1200px` (or your preferred max)
   * `margin: 0 auto` (centers horizontally)
   * Horizontal padding for mobile edges

This allows backgrounds to extend full width while content stays contained.

### Naming Convention

Use clear names for organization:

* **Plural** for parent containers: `Cards`, `Features`, `Testimonials`
* **Singular** for individual items: `Card`, `Feature`, `Testimonial`

***

## Learning CSS Properties

All properties in Webstudio's Style Panel use standard CSS names. To learn more about any property:

1. **Hover** over the property name for a tooltip explanation
2. **Search online** for "CSS \[property-name]" (e.g., "CSS flex-wrap")
3. Resources like [MDN Web Docs](https://developer.mozilla.org/en-US/docs/Web/CSS) provide detailed documentation

{% hint style="info" %}
Creating layouts is primarily done through styling, not components. You won't find a "columns" component — instead, you create columns by applying flex properties to boxes.
{% endhint %}

***

## Visual Indicators

The Style Panel uses colors to indicate property states:

| Color        | Meaning                                                                   |
| ------------ | ------------------------------------------------------------------------- |
| **Blue**     | Property is set on the current element/token                              |
| **Orange**   | Property is inherited from a parent or cascading from a larger breakpoint |
| **No color** | Property is using browser default                                         |

***

## Related Resources

* [Responsive design](/university/foundations/responsive-design) — Working with breakpoints
* [Design tokens](/university/foundations/design-tokens) — Creating reusable styles
* [Custom classes & attributes](/university/foundations/custom-classes-and-attributes) — Advanced styling options


# Layout & Grid

Learn how to create two-dimensional layouts using CSS Grid in Webstudio — define columns, rows, named areas, and position child elements visually.

> See [MDN: CSS Grid Layout](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_grid_layout)

CSS Grid is a two-dimensional layout system that lets you control both columns and rows simultaneously. While [Flexbox](/university/foundations/layout-and-flexbox) excels at one-directional flows, Grid is ideal for complex page structures like dashboards, magazine layouts, and holy grail patterns.

***

## When to Use Grid vs Flexbox

| Use case                                             | Recommended                                        |
| ---------------------------------------------------- | -------------------------------------------------- |
| Horizontal or vertical list of items                 | Flexbox                                            |
| Card grid that wraps responsively                    | Either (Flexbox with wrap or Grid with `auto-fit`) |
| Page-level structure (header, sidebar, main, footer) | Grid                                               |
| Precise cell placement and overlapping               | Grid                                               |
| Named layout regions                                 | Grid                                               |

***

## Enabling Grid

1. Select the **parent** container
2. Go to **Style Panel** → **Layout** section
3. Change **Display** to `Grid`

***

## Grid Generator

Click the **grid preview button** at the top of the Layout section to open the Grid Generator. It provides three ways to create a grid:

### Size selector

Hover over the interactive cell grid (up to 12 columns × 8 rows) and click to set the number of columns and rows. All tracks are set to `1fr`.

<figure><img src="https://330243581-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTep4EGj6hSC54ozHujyK%2Fuploads%2Fgit-blob-33c0791f388ae6a4e6843c55acce6262ff1e55a4%2Fgrid-generator.png?alt=media" alt="Grid Generator with size selector"><figcaption><p>Grid Generator size selector</p></figcaption></figure>

### Presets

Six built-in layout presets with visual thumbnails:

| Preset               | Description                                                                       |
| -------------------- | --------------------------------------------------------------------------------- |
| **Fluid sidebar**    | Sidebar that shrinks to fit its content alongside a flexible main area            |
| **Page stack**       | Single-column layout with auto-sized header, flexible body, and auto-sized footer |
| **Holy grail**       | Classic header/sidebar/main/aside/footer layout using named areas                 |
| **Responsive cards** | Auto-wrapping cards that maintain a minimum size                                  |
| **Feature section**  | Auto-wrapping feature blocks with wider minimum size                              |
| **Footer columns**   | Auto-wrapping narrow columns for footer links                                     |

<figure><img src="https://330243581-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTep4EGj6hSC54ozHujyK%2Fuploads%2Fgit-blob-85b1e296eef1d37929e3754d4bf5d7387b56484b%2Fgrid-presets.png?alt=media&amp;token=b6b27096-b168-47b5-b7ce-8c00b65d6870" alt="Grid preset thumbnails"><figcaption><p>Built-in grid presets</p></figcaption></figure>

### Fill grid

Click **Fill grid** to automatically insert child div elements into every empty grid cell — useful for quickly populating a layout.

***

## Grid Settings

Click **Configure grid** to open the detailed track editor. It contains four collapsible sections:

<figure><img src="https://330243581-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTep4EGj6hSC54ozHujyK%2Fuploads%2Fgit-blob-70610992dd613592a405ccd3b41221449192bd90%2Fgrid-settings.png?alt=media&amp;token=99b2b685-8f0f-4cd8-9069-3579574c5355" alt="Grid Settings panel with column and row tracks"><figcaption><p>Grid Settings — column and row track editors</p></figcaption></figure>

### Columns

Define explicit column tracks. Each track shows its value (e.g., `1fr`, `200px`, `minmax(250px, 1fr)`).

* Click a track to edit its value in a floating panel
* Enable **Use min/max** to set a `minmax()` value with separate Min and Max inputs
* Drag tracks to reorder
* Use **+** to add a track and **−** to remove
* Hover over a track to highlight it on the canvas

### Rows

Same controls as Columns, applied to row tracks.

### Auto columns / Auto rows

Configure implicit track sizes for content placed outside the explicit grid (via `grid-auto-columns` and `grid-auto-rows`). These tracks cannot be reordered or added/removed — they define the size of any automatically created tracks.

### Areas

Manage named grid areas (sets `grid-template-areas` on the container):

* Click **+** to add a new named area — it automatically finds a non-overlapping position
* Click an area to edit its **name** and **position** (column start/end, row start/end)
* Use the **visual area picker** to click and drag across grid cells to define the area's span
* Occupied areas appear in red; the selected area appears in blue
* Hover over an area to highlight it on the canvas

<figure><img src="https://330243581-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTep4EGj6hSC54ozHujyK%2Fuploads%2Fgit-blob-7ce2c5538a0368ba5f2fc7eb16e4399454458f3d%2Fgrid-areas.png?alt=media&amp;token=6844464f-57a6-4ecb-8b04-d006cbdd21f2" alt="Named grid areas list"><figcaption><p>Named grid areas</p></figcaption></figure>

<figure><img src="https://330243581-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTep4EGj6hSC54ozHujyK%2Fuploads%2Fgit-blob-c2aca2d1e046ddbaf5c3116dac78efc0d93be34d%2Fgrid-area-picker.png?alt=media&amp;token=60c3f05c-8b2d-471c-85f0-5d61cecd4bac" alt="Visual area picker with selected and occupied areas"><figcaption><p>Visual area picker — blue is the selected area, red indicates occupied cells</p></figcaption></figure>

{% hint style="info" %}
Named areas make it easy to position children by name instead of line numbers. Use the **Holy grail** preset to see named areas in action.
{% endhint %}

***

## Alignment

The Layout section provides alignment controls for distributing items within the grid:

### Visual alignment widget

A 3×3 clickable grid that sets **Align items** and **Justify items** simultaneously — click a cell to align all children to that position.

### Alignment properties

| Property            | Axis                | Controls                                        |
| ------------------- | ------------------- | ----------------------------------------------- |
| **Justify items**   | Inline (horizontal) | Start, Center, End, Stretch                     |
| **Align items**     | Block (vertical)    | Start, Center, End, Stretch, Baseline           |
| **Justify content** | Inline (horizontal) | Start, Center, End, Space Between, Space Around |
| **Align content**   | Block (vertical)    | Start, Center, End, Space Between, Space Around |
| **Grid auto flow**  | —                   | Row, Column, Row Dense, Column Dense            |

***

## Gap

Two inputs control spacing between grid tracks:

* **Column gap** — horizontal spacing between columns
* **Row gap** — vertical spacing between rows

Click the **link icon** between them to keep both values in sync.

***

## Grid Child

When you select an element whose parent is a grid container, the **Grid child** section appears in the Style Panel. It controls how the child is positioned within the grid.

<figure><img src="https://330243581-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTep4EGj6hSC54ozHujyK%2Fuploads%2Fgit-blob-7abe0a4713951b901b8f823d992598f71d54ec11%2Fgrid-child.png?alt=media&amp;token=8c1ebd5b-ad49-4231-aa56-af162fbdf0be" alt="Grid Child section with position modes"><figcaption><p>Grid Child section</p></figcaption></figure>

### Position modes

A toggle at the top switches between three modes:

#### Auto

The child flows into the next available cell. Set **Column span** and **Row span** to make it occupy multiple tracks.

#### Area

Select a **named area** from the dropdown to place the child into that region. All four placement properties are set to the area name automatically.

<figure><img src="https://330243581-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTep4EGj6hSC54ozHujyK%2Fuploads%2Fgit-blob-dbdf7bba71e7f64f81ad8c4b5f6a71d45ba89d5f%2Fgrid-child-area.png?alt=media&amp;token=f3e83318-ce4c-487c-a2d7-def3a8339084" alt="Grid Child in Area mode with area dropdown"><figcaption><p>Grid Child — Area mode with named area dropdown</p></figcaption></figure>

{% hint style="warning" %}
The Area dropdown only lists areas defined in the parent's Grid Settings. If no areas are defined, a message explains how to add them.
{% endhint %}

#### Manual

Set exact grid line numbers for **Column start**, **Column end**, **Row start**, and **Row end**. A **visual area picker** below the inputs lets you click grid cells to set the position interactively.

### Alignment

* **Align self** — Override the parent's `align-items` for this child (Auto, Start, Center, End, Stretch, Baseline)
* **Justify self** — Override the parent's `justify-items` for this child (Auto, Start, Center, End, Stretch, Baseline)
* **Order** — Change the visual rendering order without changing the DOM

### Select parent grid

Click the button in the section header to navigate to the parent grid container — useful for switching between child and container settings.

***

## Canvas Grid Guides

When working with Grid, Webstudio displays an overlay on the canvas showing:

* Grid track lines and borders
* Track size labels
* Named area labels in the top-left cell of each area
* Highlighted tracks when hovering over items in Grid Settings

<figure><img src="https://330243581-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTep4EGj6hSC54ozHujyK%2Fuploads%2Fgit-blob-a15917f0f47b6a210f4948f2eabc3c279f35620b%2Fgrid-guides.png?alt=media&amp;token=d9058b5a-56c4-4179-9da7-42e496f939c2" alt="Canvas with grid guide overlay"><figcaption><p>Canvas grid guides showing track lines, sizes, and area names</p></figcaption></figure>

***

## Common Patterns

### Responsive card grid

1. Set display to **Grid**
2. Open the Grid Generator and select the **Responsive cards** preset
3. Add card children — they auto-wrap and maintain a minimum width

### Page layout with named areas

1. Set display to **Grid**
2. Select the **Holy grail** preset (creates header, sidebar, main, aside, footer areas)
3. Add children and set each to **Area** mode, selecting the matching area name
4. At smaller breakpoints, redefine the areas or switch to a single-column stack

### Sidebar layout

1. Set display to **Grid**
2. Select the **Fluid sidebar** preset
3. The sidebar column uses `fit-content(300px)` — it shrinks to its content but won't exceed 300px

***

## Related

* [Layout & Flexbox](/university/foundations/layout-and-flexbox) — One-dimensional layout with Flexbox
* [Responsive design](/university/foundations/responsive-design) — Working with breakpoints
* [Design tokens](/university/foundations/design-tokens) — Creating reusable styles
* [Anatomy of the builder](/university/foundations/anatomy-of-the-webstudio-builder) — Overview of all builder panels


# Responsive design

Learn how breakpoints work in Webstudio to create responsive designs that adapt to any screen size.

Webstudio uses a mobile-first approach with customizable breakpoints to create responsive designs. Styles cascade from larger to smaller breakpoints, allowing you to progressively enhance designs for bigger screens.

## Understanding Breakpoints

Breakpoints are found at the top of the Style Panel. Each breakpoint represents a screen width range.

### Default Breakpoints

* **Base** (≥1280px) - Desktop/large screens
* **1280px** - Laptop/medium desktop
* **991px** - Tablet landscape
* **767px** - Tablet portrait
* **479px** - Mobile

{% hint style="info" %}
You can customize breakpoints in Project settings to match your design needs.
{% endhint %}

## Style Cascading

Styles cascade **down** to smaller breakpoints:

* Styles set on **Base** apply to all breakpoints
* Styles set on **991px** apply to 991px and all smaller breakpoints
* Each breakpoint can override inherited styles

### Visual Indicators

The Style Panel uses colors to show where values come from:

| Color      | Meaning                            |
| ---------- | ---------------------------------- |
| **Blue**   | Set on the current breakpoint      |
| **Orange** | Inherited from a larger breakpoint |

Hover over any property label to see exactly where the value comes from, including the breakpoint, token, and instance.

## Common Responsive Patterns

### Flex Direction Change

Convert horizontal layouts to vertical on mobile:

1. On Base breakpoint: `flex-direction: row`
2. On mobile breakpoint: `flex-direction: column`

### Show/Hide Elements

Display different elements on different screen sizes:

1. Select the element to hide on mobile
2. Go to mobile breakpoint
3. Set `display: none`

To show something only on mobile:

1. On Base: `display: none`
2. On mobile: `display: flex` (or `block`)

### Font Size Adjustments

Scale typography for readability:

* Base: `font-size: 48px` for hero headings
* Mobile: `font-size: 32px`

### Container Max-Width

Adjust content width per breakpoint:

* Base: `max-width: 1200px`
* Tablet: `max-width: 100%` with padding

## Important Concepts

### Only Styles Are Affected

Breakpoints only affect **styles**. Settings and component configurations are universal across all breakpoints. For example:

* Link URLs are the same on all breakpoints
* Image sources don't change per breakpoint
* Component settings apply everywhere

### Responsive Layout Techniques

Use these CSS properties for flexible layouts:

* **Flex wrap**: Items wrap to new rows when space is limited
* **Gap**: Consistent spacing that works with wrapping
* **Percentage widths**: Elements scale proportionally
* **Min/max widths**: Set boundaries for flexible sizing

## Workflow Tips

1. **Start from Base** - Design your desktop layout first
2. **Work downward** - Progressively adjust for smaller screens
3. **Use tokens wisely** - Apply breakpoint-specific styles as Local overrides when needed
4. **Test often** - Preview at each breakpoint using the Preview button

{% hint style="warning" %}
When you select a breakpoint and make style changes, those changes affect the current breakpoint AND all smaller breakpoints (unless overridden).
{% endhint %}

## Advanced: Media Conditions

Webstudio supports full media query conditions beyond just screen width, enabling designs that respond to device capabilities and user preferences.

### Creating Custom Breakpoints

1. Click on any breakpoint in the Style Panel
2. Click the **+** button to add a new breakpoint
3. Choose from predefined conditions or create custom ones

### Available Media Conditions

Select from predefined conditions or type a custom one:

| Category           | Values                                                                         |
| ------------------ | ------------------------------------------------------------------------------ |
| **Orientation**    | `portrait`, `landscape`                                                        |
| **Color Scheme**   | `dark`, `light`                                                                |
| **Reduced Motion** | `reduce`, `no-preference`                                                      |
| **Contrast**       | `more`, `less`, `no-preference`                                                |
| **Hover**          | `hover`, `none`                                                                |
| **Any Hover**      | `hover`, `none`                                                                |
| **Pointer**        | `coarse`, `fine`, `none`                                                       |
| **Any Pointer**    | `coarse`, `fine`, `none`                                                       |
| **Display Mode**   | `fullscreen`, `standalone`, `minimal-ui`, `browser`                            |
| **Width / Height** | `min-width`, `max-width`, `min-height`, `max-height` (set via the width input) |

### Example: Dark Mode Support

1. Click on any breakpoint in the Style Panel, then click **+**
2. Select **Color Scheme: Dark** from the condition dropdown
3. Give it a label like "Dark"
4. Select this breakpoint and style your dark theme — the canvas immediately previews it
5. Your published site automatically adapts to the user's system preference

### Example: Touch Device Styles

1. Create a breakpoint with condition **Pointer: Coarse**
2. Increase tap target sizes (minimum 44×44px)
3. Adjust hover-dependent interactions for touch

### Example: Reduced Motion

1. Create a breakpoint with condition **Reduced Motion: Reduce**
2. Disable or simplify animations for users who prefer less motion
3. Improves accessibility for users with vestibular disorders

### Example: PWA Standalone Mode

1. Create a breakpoint with condition **Display Mode: Standalone**
2. Hide browser-specific navigation elements
3. Adjust layout for the standalone app experience

### Live Preview in the Canvas

When you select a condition-based breakpoint, Webstudio **automatically simulates** that condition in the canvas. For example:

* Select a **Dark Mode** breakpoint → the canvas immediately shows your dark styles
* Select a **Portrait** breakpoint → orientation-specific styles apply
* Select a **Reduced Motion** breakpoint → animation changes are visible

No extra toggles or settings are needed — selecting the breakpoint is enough. When you switch back to a width-based breakpoint, the canvas returns to normal.

### How the Simulator Works

The breakpoint simulator dynamically modifies CSS media queries in real-time to preview condition-based styles:

* When you select a condition breakpoint, matching media rules are replaced with always-true queries
* Non-matching rules are made always-false
* This allows you to preview dark mode, reduced motion, and other conditions without changing system settings
* Original media queries are automatically restored when you switch to a different breakpoint

The simulation affects **only** the builder canvas and does not change your browser, device, or system settings.

{% hint style="info" %}
The simulation only affects the builder canvas preview. Published sites use the actual media queries and respond to real user preferences and device capabilities.
{% endhint %}

{% hint style="info" %}
Media conditions can be combined. For example, you could create a breakpoint that applies only to small screens in landscape orientation.
{% endhint %}

## Related Videos

{% embed url="<https://www.youtube.com/watch?v=gxCAfWdULzw>" %}


# Breakpoints

Add, edit, and manage breakpoints to control how your site responds to different screen sizes and conditions.

Breakpoints are found at the top of the builder canvas. They define the screen widths (and other media conditions) at which your site's styles change. Webstudio is **desktop-first by default** — Base has no media condition and applies everywhere, and the default breakpoints use max-width to adjust styles as the screen gets smaller. You can change this by adding min-width breakpoints instead, making your project mobile-first.

For guidance on how to use breakpoints to build responsive layouts, see [Responsive design](/university/foundations/responsive-design).

## Two types of width breakpoints

**Max-width breakpoints** apply styles when the screen is at or below the specified width. This is the **desktop-first** approach — you design for large screens first on Base, then add max-width breakpoints to adjust styles as the screen gets smaller.

**Min-width breakpoints** apply styles when the screen is at or above the specified width. This is the **mobile-first** approach — you design for small screens first, then add min-width breakpoints to adjust as the screen gets larger.

You can mix both types in one project if needed.

## Default breakpoints

The default breakpoints use the **max-width** (desktop-first) approach:

| Breakpoint           | Condition          | Description                                                     |
| -------------------- | ------------------ | --------------------------------------------------------------- |
| **Base**             | None (default)     | Applies to all screen sizes — the starting point for all styles |
| **Tablet**           | `max-width: 991px` | Screens 991px wide and below                                    |
| **Mobile landscape** | `max-width: 767px` | Screens 767px wide and below                                    |
| **Mobile portrait**  | `max-width: 479px` | Screens 479px wide and below                                    |

## How styles cascade

Styles cascade **in the direction that narrows the match**:

* With **max-width** breakpoints: styles flow from Base (largest) downward to smaller breakpoints. A style set on Base applies everywhere; a style on the 479px breakpoint only applies at 479px and below.
* With **min-width** breakpoints: styles flow upward. A style set on a 768px min-width breakpoint applies to all screens 768px and wider.

Each breakpoint can override styles inherited from the previous one in the cascade.

## Adding a breakpoint

1. Click any breakpoint in the top bar to open the breakpoint menu.
2. Click **+** to add a new breakpoint.
3. Set a **min-width** or **max-width** value, or choose a **media condition** (see below).
4. Give it a label.

<picture><source srcset="/files/zyKYfXP7RW3KBTOM6MDU" media="(prefers-color-scheme: dark)"><img src="https://330243581-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTep4EGj6hSC54ozHujyK%2Fuploads%2Fgit-blob-493a504b09014a4fb1e35638394f2a7eeaedc49e%2Fbreakpoints-edit-light.png?alt=media" alt="Breakpoints dialog showing names, minimum or maximum widths, and custom conditions"></picture>

## Editing and deleting breakpoints

Click any breakpoint to open its settings. You can change the width, label, or media condition. Breakpoints can be deleted unless they are the base breakpoint.

## Custom media conditions

Beyond screen width, Webstudio supports any CSS media condition. This lets you style for user preferences and device capabilities:

| Category           | Conditions                                          |
| ------------------ | --------------------------------------------------- |
| **Color scheme**   | `dark`, `light`                                     |
| **Reduced motion** | `reduce`, `no-preference`                           |
| **Orientation**    | `portrait`, `landscape`                             |
| **Contrast**       | `more`, `less`, `no-preference`                     |
| **Pointer**        | `coarse`, `fine`, `none`                            |
| **Display mode**   | `fullscreen`, `standalone`, `minimal-ui`, `browser` |

### Example: dark mode

1. Add a breakpoint, select **Color Scheme: Dark**.
2. Label it "Dark".
3. Select it and style your dark theme — the canvas immediately previews dark mode.
4. The published site automatically switches based on the visitor's system preference.

### Example: reduced motion

1. Add a breakpoint with condition **Reduced Motion: Reduce**.
2. Disable or simplify animations for affected users.
3. Improves accessibility for users with vestibular disorders.

## Related

* [Responsive design](/university/foundations/responsive-design) – How to use breakpoints to build responsive layouts
* [Style panel](/university/foundations/style-panel) – Applying styles at each breakpoint

## Style label colors

When a breakpoint is active, style property labels in the Style Panel use color to show where a value comes from.

<figure><img src="https://330243581-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTep4EGj6hSC54ozHujyK%2Fuploads%2Fgit-blob-1214e7c685cb707fded0ed32772edf899a99ef55%2Fbreakpoints-cascade.png?alt=media&amp;token=a33e417f-81de-4ad5-b4bd-a9ef900cade3" alt="Style Panel property label in orange indicating a style inherited from a larger breakpoint"><figcaption><p>An orange label means the value is inherited from a larger breakpoint</p></figcaption></figure>

See \[Label colors]\(anatomy-of-the-webstudio-builder.md#label-colors) for the full reference. Hover any label to see a tooltip with the exact source.

{% hint style="warning" %}
Mixing `min-width` and `max-width` breakpoints in the same project makes style cascading harder to reason about. Stick to one direction for maintainability.
{% endhint %}

## Related

* [Responsive design](/university/foundations/responsive-design) – How to build layouts that adapt to different screen sizes
* [Style panel](/university/foundations/style-panel) – Apply styles at the selected breakpoint
* [Label colors](/university/foundations/anatomy-of-the-webstudio-builder#label-colors) – Full reference for property label colors
* [Project settings](/university/foundations/project-settings) – Breakpoints can also be managed via Project settings


# CSS variables

CSS variables allow you to attach a value to a variable and reuse that variable throughout the Style Panel inputs.

{% hint style="info" %}
**Hint:** [Data variables](/university/foundations/variables) are different than CSS variables. They enable the reuse of data in the Settings tab.
{% endhint %}

{% embed url="<https://www.youtube.com/watch?v=NrU_9BZytQY>" %}

Why Use CSS variables?

* **Consistency** – Define a value once, like a color, and access that variable in every style input field.
* **Speed** – Design quickly by selecting predefined variables from autocomplete.
* **Experimentation** – Arrow through autocomplete and get real-time rendering of that variable to see which value looks best in your design.
* **Parent-child interactions** – Have full control over the styles of all child instances when interacting with the parent.

## CSS variables vs. Tokens

The concept of “reusability” is present in both CSS variables and [Tokens](/university/foundations/design-tokens), but they are different and complement each other exceptionally well.

Let’s think of these two concepts as layers or building blocks.

### Layer 1: CSS variables

**CSS variables are the bottom layer. They comprise individual variable names and values** often used for sizes, colors, and other styles with many input options. Craft distinguishes theme variables, which contain theme-specific inputs, from semantic variables, which describe how a value is used.

For colors, define a small set of theme variables and derive semantic variables from them. For example, `--theme-accent` can control `--background-accent`, `--border-focus`, and their interaction states. Tokens consume the semantic variables, so changing a theme input updates the complete system without editing each color independently.

### Layer 2: Tokens

[**Tokens**](/university/foundations/design-tokens) **are the next layer. They package up&#x20;*****multiple*****&#x20;styles.** Craft calls these composite Tokens. A `Card` Token may include padding, color, and gap styles, for example.

The values you enter for each style should be defined as variables. This approach ensures consistent designs and allows you to update a value in one place, with the change automatically reflected wherever the variable is used.

With CSS variables, Tokens now often take a more semantic approach, such as calling them `Card`, `Team Member`, or `Testimonial`. Without CSS variables, Tokens were a blend between semantic and utility, such as `padding-medium`, `font-size-small`, and `Card`.

{% hint style="info" %}
**Tip:** While some utility Tokens will still be present, the majority of Tokens should be semantic.
{% endhint %}

## Creating variables

{% hint style="warning" %}
Before you create custom variables, review [Craft](/university/craft), Webstudio's public specification for theme variables, semantic variables, composite Tokens, and reusable project architecture.
{% endhint %}

A CSS variable is defined in the Advanced section by using two dashes followed by the variable name, like this:

```css
--gray-5
```

{% hint style="info" %}
This syntax is not unique to Webstudio. It’s the official [CSS variable](https://developer.mozilla.org/en-US/docs/Web/CSS/Using_CSS_custom_properties) syntax.
{% endhint %}

Then, the variable's value can be anything such as a color, gradient, duration, size, or number.

<figure><img src="https://330243581-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTep4EGj6hSC54ozHujyK%2Fuploads%2Fgit-blob-aedd142a7c6b54e981a381ead6e66765ae1840fb%2Fcss-var-example.png?alt=media&amp;token=c9128478-d772-476e-b371-289d596361be" alt="CSS variable" width="319"><figcaption><p>Creating a variable and assigning a value</p></figcaption></figure>

## Using variables

Once the variable is defined, you can use it on the instance it was defined on and any of its children.

The variables are available in the autocomplete, so you can access variables by typing them in.

<figure><img src="https://330243581-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTep4EGj6hSC54ozHujyK%2Fuploads%2Fgit-blob-00f7ede4fce1e2bf19a2b7168dc84cbd46af193f%2Fcss-var-usage.png?alt=media" alt="Using a CSS variable" width="318"><figcaption></figcaption></figure>

The autocomplete search algorithm is very flexible, letting you search by any of the following:

* `--`
* `var`
* `gray` (variable name)

You can search any part of your variable, and autocomplete will show you the proper results.

{% hint style="info" %}
The syntax for *displaying* a variable is`var(--my-var)`, though it's much faster to search using `--`. Webstudio automatically handles the conversion to ensure proper output.
{% endhint %}

### Using data to set CSS variables

The common pattern is to generate a `<style>` tag via an [HTML Embed](/university/core-components/html-embed) expression, populate that tag with CSS variable definitions, and then consume those variables in the Advanced section or any style field. You can learn more about writing the bindings themselves in the [Expression editor](/university/foundations/expression-editor) documentation.

The general pattern looks like this:

1. Create the data variable with CSS variables you intend to use (you can do that on HTML Embed instance or any parent). You can use a JSON type or Resource type depending on where your data is coming from.

   <figure><img src="https://330243581-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTep4EGj6hSC54ozHujyK%2Fuploads%2Fgit-blob-6c25aff6bfaa506b4b2993f9748d560b01bda74c%2Fcreate-data-variables.png?alt=media" alt="Define CSS variables" width="400"><figcaption><p>Step 1: define your variables in the Settings panel.</p></figcaption></figure>
2. Add an HTML Embed component somewhere on the page, ideally at the top (head section is a good place but body works too) and create a binding on the code property.

   <figure><img src="https://330243581-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTep4EGj6hSC54ozHujyK%2Fuploads%2Fgit-blob-a280d9c082b5cf5135c88a99807119e581c517e6%2Fcreate-style-tag.png?alt=media" alt="Insert HTML Embed" width="400"><figcaption><p>Step 2: drop an HTML Embed onto the canvas and bind the code property.</p></figcaption></figure>
3. Write a template literal in Expression editor that produces a `<style>` block. Within the style block, interpolate whatever data you want. A couple of common patterns are shown below:

   ```js
   // key‑value style
   `<style>
     :root {
       --brand-color: ${dataVariables.themeColor};
       --feature-width: ${dataVariables.featureWidth}px;
     }
   </style>`;
   ```

   ```js
   // entire CSS string from a JSON field
   `<style>
     :root {
        ${dataVariables.variables}
     }
   </style>`;
   ```

   The `${}` expressions can reference any value accessible in the Expression editor, including nested properties and ternary logic. The second form is useful when an API returns a single string containing multiple declarations.

   > *Note:* you don’t need any special escaping; `dataVariables.variables` uses the standard JavaScript syntax to refer to a nested field. Since the HTML Embed evaluates whenever its dependencies change, the resulting `<style>` tag will update with new values. The variables it defines are now available everywhere on the page.

   <div data-gb-custom-block data-tag="hint" data-style="warning" class="hint hint-warning"><p>Variables created via an HTML Embed are not picked up by the style‑panel autocomplete. You must type the variable name manually (or copy/paste it) when referencing it later.</p></div>
4. In Advanced section or any style inputs, refer to the variables with `var(--brand-color)`.

   <figure><img src="https://330243581-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTep4EGj6hSC54ozHujyK%2Fuploads%2Fgit-blob-5debd8330062a2bbfd4a1b454510cfe9063749a7%2Fuse-variable.png?alt=media" alt="Use CSS variable in style input" width="400"><figcaption><p>Step 5: reference the variable in any style value input.</p></figcaption></figure>

This technique is powerful for theming, A/B testing, or applying dynamic values such as colors or dimensions fetched from an API and managed by an external CMS.

### Scope

CSS variables, by nature, are available in the current instance and any of the children.

#### Global Variables

<picture><source srcset="/files/YzsTKXTd8nnTmYnHVHno" media="(prefers-color-scheme: dark)"><img src="https://330243581-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTep4EGj6hSC54ozHujyK%2Fuploads%2Fgit-blob-47b20f3aa5925c1fac67074b5e3f9f940c0241d3%2Fnavigator-panel-light.png?alt=media" alt="Navigator with Global root above Body and the page instance tree"></picture>

Most variables should be defined on the Global Root, which is the highest level of the page and the same for every page. That way, you can define `--my-color`, and it's available on every instance on every page.

### Local variables

Some variables are only needed on a specific section or page. You can define these variables on a common ancestor of where they need to be accessed, such as a Box/wrapper.

Here are some use cases for local variables:

* Changing a child's design when interacting with the parent (see [Parent-child interactions](#parent-child-interactions) for more info).
* Doing an A/B test without modifying the entire design system
* Running a seasonal promotion and modifying colors for a section

## Parent-child interactions

With CSS variables, you can interact with the parent and modify the styles of any of the children.

Prefer CSS variables over a [descendant custom state](/university/foundations/states-and-selectors#custom-states) such as `:hover .button` for parent-child interactions. They keep each property on the child that uses it and are especially useful when one parent state coordinates several properties or children. Define a variable on the parent, use it for the relevant property on each child, and change the variable's value in the parent's `:hover` state.

<figure><img src="https://330243581-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTep4EGj6hSC54ozHujyK%2Fuploads%2Fgit-blob-256cec2292724c8c5e7346fa26bd8d63a14b05ed%2Fparent-child-demo.gif?alt=media" alt="Hovering a link changes its child icon color, background, and arrow visibility"><figcaption><p>Hovering the link and the children change (icon color, icon bg, and arrow appears)</p></figcaption></figure>

{% embed url="<https://youtu.be/rg49mmDvdlE>" %}
Video tutorial
{% endembed %}

Here are the high-level steps to accomplish this pattern:

1. Take note of the various properties you want to change such as color and background color.
2. Create a variable on the parent for each style property such as `--child-color` and `--child-bg`. Leave the values empty for now.
3. Add the variables to the various children's style inputs, such as setting background to `--child-bg` (no style changes will happen yet because the variables don't have values).
4. Go back to the parent, where the variables are defined, and give them values for the default state.
5. Switch to the other state, such as hover, and change the variables' values.
6. Interact with the parent and see the child change!

{% hint style="info" %}
For step 2, it's possible to define variables and assign values all at once, but it's better to add the variables to the children first. If the variables aren't added to the children, the assigned values won't render anywhere, which makes it difficult to determine things like which color to use.
{% endhint %}

### Example Variable Names

For a navigation hover effect:

* `--nav-icon-bg` – Icon background color
* `--nav-icon-color` – Icon fill color
* `--nav-arrow-opacity` – Arrow visibility
* `--nav-arrow-translate` – Arrow position

### Adding Transitions

Add transitions on the **child instances** (not the parent) to smooth out the changes:

* On the icon: add transitions for `background-color` and `color` (\~200ms duration)
* On the arrow: add transitions for `opacity` and `translate` (\~200ms duration)

You can define as many variables as you want and use them on any children where they are defined to create more complex interactions.

## Related

* [States and selectors](/university/foundations/states-and-selectors) – Style hover, focus, active states and pseudo-elements
* [Design tokens](/university/foundations/design-tokens) – Package multiple styles for reuse
* [Modes](/university/foundations/modes) – Create breakpoints for dark mode and other conditions
* [Animations](/university/foundations/animations) – Use CSS variables for hover animations
* [Anatomy of the Webstudio builder](/university/foundations/anatomy-of-the-webstudio-builder) – Learn about Global Root and the Style Panel


# Design tokens

Tokens package reusable style declarations that stay synchronized across every instance where they are applied.

{% embed url="<https://youtu.be/O8XNB2_JfaQ>" %}

## Why Tokens instead of classes? <a href="#introduction" id="introduction"></a>

If you’ve ever made a website with CSS or with Webflow, you’ve used “classes” to manage your website’s layout and visual styles. Sometimes we love classes. They give us a way to re-use styles which saves us valuable time. However, they have some limitations that make classes very frustrating to use in the context of visual development tools.

Scenario: You’re building a website with Webflow. You have two separate elements, a button, and a card, and you want to give them the same box shadow. Buttons and cards have unique styles so they each already have a unique class. What do you do?

* A: Manually configure the box shadow on the existing Button and Card classes individually. With this option you’re doing the same thing twice. It would save time if we could reuse the box shadow styles.
* B: Apply the Box Shadow class on top of the existing Button and Card classes, making a combo class. Now you can’t edit the styles on Button or Card without first removing the Box Shadow class. Don’t forget to re-apply it! And good luck managing the classes on a different breakpoint. To edit the Button or Card classes you must first remove the Box Shadow combo class, then Webflow will kick you back to the desktop breakpoint, then you select the intended breakpoint again, then make your style changes, then re-apply the combo class. Experienced Webflowers know the pain.

There’s a better way. It’s Design tokens.

## What are Tokens? <a href="#what-are-design-tokens" id="what-are-design-tokens"></a>

A Webstudio **Token** is a reusable style source that can contain multiple declarations and states. [Craft](/university/craft) calls it a **composite Token** to distinguish it from individual values stored in CSS variables.

The Design Tokens Community Group also uses *design token* for values in its portable data format. Webstudio imports that format and represents its values as CSS variables or Webstudio Tokens according to their type and intended use.

* **Mix-and-match Tokens freely**: You can apply as many Tokens as you want to an instance in any order. There is no combo class silliness and no limitations with breakpoints.
* **Portable input:** The [Design Tokens Community Group format](https://design-tokens.github.io/community-group/format/) provides a format that tools can exchange. Webstudio can import DTCG data and Figma Variables API exports.

## Import design tokens

Webstudio can import token data from the [Design Tokens Community Group format](https://design-tokens.github.io/community-group/format/) and Figma Variables API exports. Copy the JSON document, then paste it into the Builder. Webstudio detects supported token documents and asks how to represent them:

* **Design tokens** creates reusable style tokens for composite and unambiguous style values. Other primitive values become CSS variables.
* **CSS variables** imports the values as custom properties for use in individual styles.

The importer resolves aliases and supports Figma modes and DTCG composite values such as borders, shadows, gradients, transitions, and typography. CLI and MCP integrations can additionally select modes, import every mode with qualified names, map token types to style properties, and apply a prefix or breakpoint.

If an imported name conflicts with an existing token, choose how to continue:

* **Theirs** keeps the imported token under a name with a numeric suffix.
* **Ours** skips the incoming token and keeps the Project token.
* **Merge** writes incoming styles into the existing token, with incoming values taking priority.

Review imported tokens and CSS variables before applying them throughout the Project, especially when the source contains multiple modes or aliases.

## How to use tokens <a href="#how-to-use-tokens-in-webstudio" id="how-to-use-tokens-in-webstudio"></a>

The workflow for styling Tokens in Webstudio is nearly the same as styling classes in Webflow, except better.

First, it's recommended to create [CSS variables](/university/foundations/css-variables) to use within the Tokens.

You can style your site using Local or Tokens like this:

1. Select an instance. The **Style sources** field at the top of the Style panel shows its Local styles and Tokens.

   <img src="https://330243581-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTep4EGj6hSC54ozHujyK%2Fuploads%2Fgit-blob-0bdfee23db4eaa87318e2466640b4b11dd87a5c3%2Fstyle-sources.png?alt=media" alt="Style sources field showing the Local source and applied Tokens" data-size="original">
2. To create a Token, select the Style sources field, enter a name, and press Enter.

   <img src="https://330243581-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTep4EGj6hSC54ozHujyK%2Fuploads%2Fgit-blob-df0502308135fa169fbfd20c3d2da697e094c470%2Fnew-token.png?alt=media" alt="Style sources field with a new Token name ready to be added" data-size="original">
3. Select the source you want to edit. The active source is highlighted, and new styles are written to that source.

   <img src="https://330243581-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTep4EGj6hSC54ozHujyK%2Fuploads%2Fgit-blob-75ed14cb1acc86a6320f2fb1890c54dd372c0def%2Ftokens-added.png?alt=media" alt="Style sources field with several Tokens and one active Token" data-size="original">

Use the Local source for styles that apply only to the selected instance. You can convert Local styles into a reusable Token later.

<img src="https://330243581-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTep4EGj6hSC54ozHujyK%2Fuploads%2Fgit-blob-1fc91199a3bc0e08c638c2aed09302a0aa7fdcfb%2Fconvert-to-token.png?alt=media" alt="Local style source menu with the action to convert its styles to a Token" data-size="original">

Hover a property label to see which breakpoint and style source provides its current value. See [Label colors](/university/foundations/anatomy-of-the-webstudio-builder#label-colors) for the full reference.

<img src="https://330243581-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTep4EGj6hSC54ozHujyK%2Fuploads%2Fgit-blob-f2736a219cc1ad1e9981e70342594eed6c5d4968%2Fproperty-label-tooltip.png?alt=media" alt="Style property tooltip identifying the breakpoint, Token, and instance that provide the value" data-size="original">

An empty circle means the source has no styles. A dot appears in the Local source after you add a style.

<img src="https://330243581-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTep4EGj6hSC54ozHujyK%2Fuploads%2Fgit-blob-aec3d2ea40d039eaefba9015d1cbb22e68012300%2Fempty-circle.png?alt=media" alt="Empty circle indicator beside a style source with no styles" data-size="original">

## Exporting human-readable classes

By default, Tokens are converted to atomic styles, significantly reducing the amount of CSS, ultimately leading to a faster-loading website.

While the majority of users aren't concerned with how the classes are output and should use atomic styles, they can be optionally disabled.

See [Atomic CSS](/university/foundations/project-settings#atomic-css) for more info.

## Advanced Token Techniques

### Token composition

Combine multiple tokens to create flexible, modular designs:

1. **Base Token**: Contains core styles, such as `card` with padding, background, and border radius.
2. **Variant or size Tokens**: Contain only the declarations needed for a variation, such as `is-card-featured` or `card-small`.

Apply both to an instance: The styles merge, with later tokens overriding earlier ones for conflicting properties.

**Example:** A card system

* `card` Token: padding, background, and border radius
* `card-small` Token: smaller padding
* `is-card-featured` Token: accent border color

Apply `card` and `is-card-featured` for a featured card variant.

### Token Priority (Cascading)

When multiple tokens define the same property, **the rightmost token wins**:

```
[card] [small] [featured]
       ↑        ↑
       │        └── Takes priority for any shared properties
       └── Overrides card for any shared properties
```

This allows you to build up styles modularly while maintaining precise control.

### Local Overrides

To override token styles for a specific instance:

1. Apply your tokens
2. Drag **Local** to the end (rightmost position)
3. Add your override styles on Local

Since Local is rightmost, its styles take priority over the tokens.

### Resetting Values

To remove a style from a specific token or Local:

1. Select the token in Style Sources
2. Hover over the property label
3. Click the reset icon (or use Option+click on Mac)

This removes the property from that specific token, allowing inherited values to show through.

### Duplicating Tokens

Create variations from existing tokens:

1. Select the token in Style Sources
2. Open the token menu (three dots)
3. Choose **Duplicate**
4. Rename and modify the duplicate

This is faster than creating tokens from scratch when building design systems.

### Token Conflict Resolution

When pasting content from another project or copying between pages, Webstudio intelligently handles token conflicts:

**Automatic Resolution**

* If a pasted token has the same name AND same styles as an existing token, they're automatically merged
* This prevents duplicate tokens when copying similar components

**Numeric Suffix**

* If a pasted token has the same name but different styles, a numeric suffix is added (e.g., "Button" becomes "Button-1")
* This preserves both your existing styles and the pasted styles

**Find Duplicate Tokens** Use Commands & search (⌘+K) and search for "duplicate tokens" to find tokens with identical styles but different names. This helps clean up your token library.

## Related

* [States and selectors](/university/foundations/states-and-selectors) – Style hover, focus, active states and pseudo-elements
* [CSS variables](/university/foundations/css-variables) – Define reusable style values to use within Tokens
* [Anatomy of the Webstudio builder](/university/foundations/anatomy-of-the-webstudio-builder) – Learn about the Style Panel and Style Sources
* [Project settings](/university/foundations/project-settings) – Configure atomic CSS output
* [Commands & search](/university/foundations/commands-and-search) – Quickly manage and search Tokens


# Style panel

Use the Style Panel to apply CSS styles to any instance on your page.

The Style Panel is on the right side of the builder. It exposes every CSS property visually and applies styles to the currently selected instance. All style changes are scoped by the active [breakpoint](/university/foundations/breakpoints) and style source.

<picture><source srcset="/files/cfHDvv3Mmbzjh5Rxb9Lk" media="(prefers-color-scheme: dark)"><img src="https://330243581-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTep4EGj6hSC54ozHujyK%2Fuploads%2Fgit-blob-25a7e051ecdd904edd1f0797f38f0e5c02febec9%2Fstyle-panel-light.png?alt=media" alt="Style panel showing style sources, layout, grid child, spacing, and size controls"></picture>

## Style sources

At the top of the Style Panel is the **style source bar**, which determines where styles are saved and how reusable they are. There are two types of style sources:

* **Local** — styles apply only to this instance. See [Design tokens](/university/foundations/design-tokens) for the full explanation of Local vs. Tokens.
* **Tokens** — reusable style groups shared across multiple instances. See [Design tokens](/university/foundations/design-tokens).

[CSS variables](/university/foundations/css-variables) are not a style source — they are named values (e.g. `--color-brand`) defined on any instance in the Advanced section and referenced inside style inputs across the site.

Style label colors indicate where each value comes from. See [Label colors](/university/foundations/anatomy-of-the-webstudio-builder#label-colors) for the full reference.

## Sections

The Style Panel is organized into collapsible sections. A dot on the section title indicates at least one property in that section has a value set.

### Layout

Controls how the selected instance arranges its children.

* **Display** — sets the layout mode: `block`, `flex`, `grid`, `inline`, `inline-block`, `inline-flex`, `inline-grid`, `none`, etc.
* **Direction** (`flex-direction`) — row or column, and their reversed variants
* **Wrap** (`flex-wrap`) — whether flex children wrap to the next line
* **Align items** — cross-axis alignment of children
* **Justify items** — inline-axis alignment of children (grid)
* **Justify content** — distribution of children along the main axis
* **Align content** — distribution of wrapped lines
* **Gap** (`row-gap`, `column-gap`) — space between children
* **Grid auto flow** — automatic placement direction for grid items
* **Grid auto columns / rows** — size of implicitly created grid tracks
* **Grid template columns / rows** — explicit grid track definitions

### Flex child

Visible when the selected instance is inside a flex container. A link in the section header lets you quickly jump to the parent flex container.

* **Align self** — overrides the parent's `align-items` for this instance only
* **Flex grow** — proportion of available space this instance should take up
* **Flex shrink** — how much this instance shrinks relative to others when space is tight
* **Flex basis** — the default size before growing or shrinking
* **Order** — visual order within the flex container (does not affect DOM order)

### Grid child

Visible when the selected instance is inside a grid container.

* **Column start / end** (`grid-column-start`, `grid-column-end`) — which grid column lines to span
* **Row start / end** (`grid-row-start`, `grid-row-end`) — which grid row lines to span
* **Align self** — vertical alignment within its grid cell
* **Justify self** — horizontal alignment within its grid cell
* **Order** — visual order within the grid

### Size

Controls the dimensions and overflow of the instance.

* **Width / Height** — explicit size (supports px, %, rem, vh, vw, auto, etc.)
* **Min width / Min height** — minimum size the instance can shrink to
* **Max width / Max height** — maximum size the instance can grow to
* **Overflow X / Y** — `visible`, `hidden`, `scroll`, `auto` — controls what happens when content is larger than the box
* **Object fit** — how an image or video fills its box (`cover`, `contain`, `fill`, `none`)
* **Object position** — alignment of the media within its box
* **Aspect ratio** — maintains a fixed width-to-height ratio

### Space

Controls padding (inside the border) and margin (outside the border). Each property supports setting all four sides at once or individually.

* **Padding** — space between the content and the border
* **Margin** — space outside the border, pushing other instances away

### Position

Controls how the instance is positioned in the document flow.

* **Position** — `static` (default), `relative`, `absolute`, `fixed`, `sticky`
* **Top / Right / Bottom / Left** — offset from the reference point (only applies to non-static positions)
* **Z-index** — stacking order (also available for flex and grid children)

### Typography

Controls text appearance.

* **Font family** — the typeface; uploaded fonts from Assets are available here
* **Font weight** — thin, regular, medium, bold, etc.
* **Font size** — supports all CSS units
* **Line height** — spacing between lines
* **Color** — text color with color picker and CSS variable support
* **Text align** — left, center, right, justify, start, end
* **Font style** — normal or italic
* **Text decoration** — underline, overline, line-through, or none
* **Letter spacing** — space between individual characters
* **Text transform** — uppercase, lowercase, capitalize, none
* **White space collapse** — controls how whitespace and line breaks are handled
* **Text wrap mode** — `wrap` or `nowrap`
* **Text wrap style** — `auto`, `balance`, `pretty`, or `stable` (controls how text wraps across lines)
* **Hyphens** — `none`, `manual`, or `auto` (whether words are hyphenated at line breaks)
* **Direction** — left-to-right or right-to-left (for RTL languages)
* **Text overflow** — `clip` or `ellipsis` (requires `white-space: nowrap` and `overflow: hidden`)

### Backgrounds

Supports multiple background layers. Each layer can be a color, image, or gradient.

* **Background color** — solid color applied behind all background layers
* **Background image** — image from Assets or an external URL
* **Gradient** — linear or radial, with full stop editor
* **Background size** — `auto`, `cover`, `contain`, or exact dimensions
* **Background position** — X and Y offset
* **Background repeat** — `repeat`, `no-repeat`, `repeat-x`, `repeat-y`, `space`, `round`
* **Background attachment** — `scroll`, `fixed`, `local`
* **Background clip** — `border-box`, `padding-box`, `content-box`, `text`
* **Background origin** — reference box for `background-position`
* **Background blend mode** — how this layer blends with layers below it

### Borders

* **Border style** — `solid`, `dashed`, `dotted`, `double`, `none`, etc. (per side or all at once)
* **Border color** — per side or all at once
* **Border width** — per side or all at once
* **Border radius** — rounds corners; set all four corners individually or together

### Outline

The outline is drawn outside the border and does not affect layout (no space is reserved for it).

* **Outline style** — `solid`, `dashed`, `dotted`, `none`, etc.
* **Outline color**
* **Outline width**
* **Outline offset** — distance between the border and the outline

### Box shadows

Add one or more `box-shadow` layers. Each layer has:

* **X / Y offset** — horizontal and vertical position
* **Blur** — softness of the shadow edge
* **Spread** — expansion or contraction of the shadow shape
* **Color**
* **Inset** — switches from an outer to an inner shadow

### Text shadows

Add one or more `text-shadow` layers. Each layer has:

* **X / Y offset**
* **Blur**
* **Color**

### Filter

CSS `filter` effects applied to the element and its content:

* Blur, Brightness, Contrast, Grayscale, Hue-rotate, Invert, Opacity, Saturate, Sepia, Drop shadow

Multiple filters can be stacked. Order matters — they are applied left to right.

### Backdrop filter

Same filter functions as Filter, but applied to the content **behind** the element (requires a non-opaque background). Commonly used for frosted-glass effects.

### Transitions

Animate style property changes smoothly. Multiple transition layers can be defined, one per animated property.

* **Property** (`transition-property`) — which CSS property to animate (e.g. `opacity`, `transform`, `all`)
* **Duration** (`transition-duration`) — how long the animation takes
* **Timing function** (`transition-timing-function`) — easing curve: `ease`, `linear`, `ease-in`, `ease-out`, `ease-in-out`, or a custom cubic-bezier
* **Delay** (`transition-delay`) — how long to wait before starting
* **Behavior** (`transition-behavior`) — controls whether discrete properties (like `display`) can be transitioned

### Transforms

Move, rotate, scale, or skew the instance without affecting layout.

* **Translate** — move along X, Y, or Z axis
* **Scale** — resize along X, Y, or Z axis
* **Transform** — raw `transform` value for rotate, skew, matrix, and combined transforms
* **Transform origin** — the point around which rotation and scaling happen
* **Backface visibility** — whether the back face is visible when rotated past 90°
* **Perspective** — depth of the 3D perspective projection
* **Perspective origin** — the vanishing point for the perspective

### Advanced

A free-form CSS property editor for anything not covered by the sections above. Type any valid CSS property name — autocomplete shows the full list — and enter its value.

Autocomplete includes standard, experimental, nonstandard, and obsolete CSS properties. A yellow warning marks any property that is not standard. Open the warning to review its status and browser-support risk, then select **Learn more on MDN** for compatibility details. Test these properties in the browsers your site supports before relying on them.

**Common uses:**

* Properties not exposed elsewhere: `cursor`, `pointer-events`, `will-change`, `content`, `list-style`, `appearance`, `resize`, `clip-path`, etc.
* Defining [CSS variables](/university/foundations/css-variables): add a property like `--my-color` with a value, and it becomes available to all child instances
* Setting properties you know by name without searching the panel

## Related

* [Design tokens](/university/foundations/design-tokens) – Local styles, tokens, and how they work together
* [CSS variables](/university/foundations/css-variables) – Named values used inside style inputs
* [Label colors](/university/foundations/anatomy-of-the-webstudio-builder#label-colors) – What the blue, orange, and gray labels mean
* [States and selectors](/university/foundations/states-and-selectors) – Style hover, focus, and other states
* [Breakpoints](/university/foundations/breakpoints) – Style at different screen sizes
* [Transforms](/university/foundations/transforms) – Translate, rotate, and scale instances


# States and selectors

Style elements differently based on user interaction (hover, focus, active) or target virtual parts of an element (::before, ::after) using states and selectors.

States and selectors let you apply styles conditionally — for example, change a button's color on hover, or add decorative content with `::before`.

They live in the **Style Sources** dropdown alongside your [Tokens](/university/foundations/design-tokens). Open it by clicking the Style Sources area above the Style Panel or pressing `⌘ + Enter` (Mac) / `Ctrl + Enter` (Windows).

<figure><img src="https://330243581-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTep4EGj6hSC54ozHujyK%2Fuploads%2Fgit-blob-abaeed6177bd08fb2d51f7cb24ea60d9541011bc%2Fstates-dropdown.png?alt=media&amp;token=c2532b3f-bf03-422e-accf-2a7f872abcb0" alt="States in the Style Sources dropdown" width="375"><figcaption><p>Available states appear at the bottom of the Style Sources dropdown</p></figcaption></figure>

## States (pseudo-classes)

States are CSS pseudo-classes that apply styles when a condition is true, such as the user hovering over an element.

### Adding a state

1. Select an instance on the canvas.
2. Open the Style Sources dropdown (`⌘ + Enter`).
3. Choose a state from the list at the bottom, or type one in.
4. The state appears as a tag in the style sources area. While it's active, every style you set applies only to that state.
5. Click the state tag again to deselect it and return to the base (default) styles.

<figure><img src="https://330243581-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTep4EGj6hSC54ozHujyK%2Fuploads%2Fgit-blob-eedde87d9fe0dbd68148d91fde14fd10b7846caf%2Fstate-active.png?alt=media&amp;token=55a6e4cf-81d5-4b80-898d-c3f912dee3bc" alt="Active hover state on a Button" width="375"><figcaption><p>The :hover state is selected — styles set now only apply on hover</p></figcaption></figure>

### Custom states

You can type any valid CSS pseudo-class into the Style Sources dropdown. If it's not in the predefined list, Webstudio will accept it as a custom selector.

{% hint style="info" %}
Custom states can target descendants while staying scoped to the selected instance. For example, `:hover .button` targets descendants with the [custom class](/university/foundations/custom-classes-and-attributes) `button` while the selected instance is hovered, and `:hover > .icon` targets only direct children with the custom class `icon`.

Prefer [CSS variables for parent-child interactions](/university/foundations/css-variables#parent-child-interactions). They keep each property on the child that uses it, can coordinate several children from one parent state, and do not couple the interaction to descendant classes or structure. Use a descendant custom state when the interaction specifically requires selector-based targeting.

Start the selector with a pseudo-class, pseudo-element, or attribute selector for the selected instance. You can then use a descendant ( ) or child (`>`) combinator. Selector lists, selectors that start with a type, class, or ID, and sibling combinators (`+` and `~`) are rejected because they can target elements outside the selected instance.
{% endhint %}

## Pseudo-elements

Pseudo-elements target virtual parts of an element, such as inserting content before or after it, or styling the first letter.

### Adding a pseudo-element

1. Select an instance.
2. Open the Style Sources dropdown (`⌘ + Enter`).
3. Type `::before`, `::after`, or another pseudo-element — autocomplete will suggest matches.
4. The pseudo-element appears as a tag. While selected, styles apply to that virtual element.

<figure><img src="https://330243581-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTep4EGj6hSC54ozHujyK%2Fuploads%2Fgit-blob-c7f94b0df74766a3585bcfb19d97413d8b034a45%2Fpseudo-element-autocomplete.png?alt=media&amp;token=1cdd4f50-8c5d-439c-a6f7-97f04173430a" alt="Pseudo-element autocomplete" width="375"><figcaption><p>Type :: to see available pseudo-elements</p></figcaption></figure>

### Common pseudo-elements

| Pseudo-element   | What it targets                             |
| ---------------- | ------------------------------------------- |
| `::before`       | Virtual element inserted before the content |
| `::after`        | Virtual element inserted after the content  |
| `::placeholder`  | Placeholder text in inputs and textareas    |
| `::selection`    | Text selected/highlighted by the user       |
| `::first-letter` | First letter of a block element             |
| `::first-line`   | First line of a block element               |
| `::marker`       | Bullet or number of a list item             |
| `::backdrop`     | Box behind a fullscreen or modal element    |

{% hint style="info" %}
`::before` and `::after` require a `content` value to be visible. Set it in the **Advanced** section — even an empty string (`""`) works.
{% endhint %}

### Example: decorative element with ::before

1. Select a Box, Link, or Button.
2. Add `::before` via the Style Sources dropdown.
3. In the **Advanced** section, set `content` to `""`.
4. Set `display` to `block` and give it a width, height, and background.
5. Deselect the `::before` tag to go back to the base styles.

<figure><img src="https://330243581-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTep4EGj6hSC54ozHujyK%2Fuploads%2Fgit-blob-d1cb3e7eb77cd6a568d4a54af8b24c35deb6afaa%2Fbefore-example.png?alt=media&amp;token=51ed8573-feee-4c95-b66a-def1f6c06deb" alt="Styling a ::before pseudo-element" width="375"><figcaption><p>A ::before element styled as a decorative bar</p></figcaption></figure>

## Combining states and tokens

States work together with [Tokens](/university/foundations/design-tokens). When you have a Token selected, adding a state applies styles for that state *within that Token*. This keeps hover styles, focus styles, and base styles neatly organized.

You can also use [CSS variables](/university/foundations/css-variables) to coordinate several parent-child style changes — define variables on the parent, then change their values on the `:hover` state so all children update at once. See [Parent-child interactions](/university/foundations/css-variables#parent-child-interactions) for details.

## Related

* [CSS variables](/university/foundations/css-variables) – Define reusable values and create parent-child hover interactions
* [Design tokens](/university/foundations/design-tokens) – Package multiple styles for reuse
* [Anatomy of the Webstudio builder](/university/foundations/anatomy-of-the-webstudio-builder) – Learn about the Style Panel and Style Sources
* [Shortcuts](/university/foundations/shortcuts) – Keyboard shortcuts including `⌘ + Enter` for Style Sources


# Variables

Define values and resources for use throughout your site.

{% hint style="info" %}
**Hint:** [CSS variables](/university/foundations/css-variables) are different than data variables. They enable the reuse of values in the Style Panel.
{% endhint %}

Define variables on an instance in the Navigator, such as Global root, Body, or Heading. Find them in **Settings > Variables**.

<picture><source srcset="/files/DtEBkS4vPLpxr8ndOmRR" media="(prefers-color-scheme: dark)"><img src="https://330243581-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTep4EGj6hSC54ozHujyK%2Fuploads%2Fgit-blob-8f8bebcf44072e486069561c3d24af0793c47d09%2Fvariables-settings-panel-light.png?alt=media" alt="Settings panel with instance properties and a Variables section listing static and dynamic variables"></picture>

## Variable scope

Variables are available within the instance they are defined on and any of its children. They are *not* available in the parent.

If you define a variable on Global root, it is available on all pages and can be used within [Slots](/university/core-components/slot).

{% hint style="info" %}
To access variables in the Page Settings, the variable must be defined on the Body of the page.
{% endhint %}

## Create and edit a variable

Select an instance in the Navigator, then use **Add variable** in **Settings > Variables**. In the **New variable** dialog, enter a **Name** and choose a **Type**. The name field suggests names already used in expressions but not yet created. Names must be unique on the same instance. You can reuse an ancestor's variable name, but a warning shows that the new variable shadows it.

Choose **String**, **Number**, **Boolean**, **JSON**, **REST resource**, **GraphQL resource**, **Assets resource**, **Email resource**, **Sitemap resource**, or **Current date resource**. Resource types may require a plan with dynamic data access. The editor changes with the type, and the right side of the dialog previews the value or resource data. You can resize or maximize the dialog.

Changes save when you close the dialog or move away from it. A new variable closes after it is created. Correct an invalid name before closing; other field errors are shown in their editors. For an existing variable, the menu offers **Copy** and, where allowed, **Delete**. Copying a resource includes its configuration, so you can paste it onto another instance or into another project. Resource editors also provide **Refresh resource data** and a **Refresh** menu action.

## Global variables

Variables defined on **Global root** are accessible across all pages. Use them for shared contact information, site settings, or resource configurations needed in [Slots](/university/core-components/slot).

### Setting up global variables

1. In the Navigator, select **Global root** above Body.
2. In **Settings > Variables**, select **Add variable**.
3. Enter a name and choose a type in the **New variable** dialog.

### Use cases

{% embed url="<https://www.youtube.com/watch?v=jRzMROoKwsQ>" %}

* **Contact email**: Define once and bind it to contact information across pages.
* **Simple CMS**: Use a JSON variable on Global root for data displayed in Slots.
* **Shared resource**: Define a resource once for use across pages.

## Variables

### System

The System variable is available by default. Forms also add variables automatically for submission data and state.

System variable contains the following:

* **Params** – Key/value pairs that are defined in Dynamic Page URLs.
* **Search** – Key/value pairs of query parameters that may exist in the URL.
* **Origin** – The URL of the current site. It will display whatever the actual URL is, so in the builder, it will be the internal wstd.io domain, but on the published site, it will be the current origin, likely a custom domain.
* **Pathname** – The current page's path (e.g., `/blog/my-post`). Useful for conditional logic based on the current URL.

### String

A String variable holds text data, which can be used for titles, descriptions, and labels. Enter the text in **Value**; the preview updates as you edit.

### Number

A Number variable stores numeric values, including integers and decimals. Enter a number in **Value**; the editor reports an empty or invalid number.

### Boolean

A Boolean variable represents a true or false value. Set **Value** with the switch, for example to control visibility.

### JSON

A JSON variable allows for structured data. Enter its value in the expression editor; syntax errors are marked there, and the evaluated value appears in the preview. It can provide items to a [Collection](/university/core-components/collection), such as a dynamic gallery.

### Resource

Choose **REST resource** to fetch data from an HTTP API. For a GraphQL API, use the [GraphQL resource](#graphql) editor.

There are several fields available to configure the fetch request.

{% hint style="info" %}
**Shortcut:** The URL field supports pasting in a cURL command. Doing so will automatically populate the various fields within the Resource. Many API docs will provide you with a cURL command, so look out for it to save time.
{% endhint %}

* URL – Where the resource is located.
* Method – GET, POST, PUT, or DELETE. Refer to the third-party API docs to find the suitable method.
* Search params – Key/value pairs used for providing additional parameters as part of the URL. Values can be bound to other variables.
* Cache max age – Define your own cache lifetime.
* Headers – Key/value pairs such `Content-Type application/json`. Refer to [Request Headers](https://developer.mozilla.org/en-US/docs/Glossary/Request_header) for more info.
* Format and body – For requests other than GET, choose **auto**, **application/json**, or **multipart/form-data** and configure the body. A Form action also shows these fields even when GET is selected because Form submissions use POST.

The preview can load the request and show its response, request details, and error diagnostics. Use **Refresh resource data** after changing inputs or to request fresh data.

{% hint style="success" %}
The requests, including any sensitive secrets like API keys, are handled on the backend and are never exposed to the client.
{% endhint %}

#### Submit a form to a resource

A new [Form](/university/core-components/form) includes two email resources. To add another destination, create a REST, GraphQL, or email resource in **Variables** on the Form or an ancestor. Then select the Form and use **Properties & Attributes > Action** to add the resource. Resources defined on the Form can bind to its `formData` and safe `browserInfo` values; ancestor resources cannot bind to those values. HTTP destinations always send POST requests when the Form is submitted, regardless of their configured method for ordinary data loading.

The Form also exposes `formState`, `results`, and `errors` in **Variables**. Use these to show the submission state and inspect the selected actions' responses. See the [Form guide](/university/core-components/form#use-form-variables) for their contents and submission behavior.

For a Form-scoped HTTP resource, leave the body unset to forward every named Form field. **Format** defaults to **auto**: text-only Form data is JSON; a body containing uploaded files becomes multipart. Choose **application/json** or **multipart/form-data** when the receiving API requires that format. JSON rejects uploaded files instead of dropping them. Multipart sends repeated values under the same field name and includes uploaded files with their filenames and bytes. Use the [Form upload guide](/university/core-components/form#send-files) for file inputs and the combined request limit.

#### Response

Once the Resource fetches data, you'll use the [Expression editor](/university/foundations/expression-editor) to bind data from the response to your website. See [Binding](/university/foundations/expression-editor#binding) for more information.

#### Dependent resources

Bind a Resource request field to another Resource variable when one request needs data from an earlier response. For example, bind an author ID from one Resource into the URL or search parameters of a second Resource that loads the author's posts.

Webstudio fetches the dependency first and evaluates the dependent request after the dependency data is available. A Resource cannot depend on itself or on another Resource that already depends on it. Collection item and component parameter variables are not available in Resource request fields.

When a Form uses a Resource action, Webstudio fetches that action's dependencies when the form is submitted, not when the page loads.

Form actions bypass Webstudio's resource cache so each submission sends a new request. Their dependencies still use the configured cache lifetime.

#### Caching

You may be wondering whether every visit to your blog results in an API call to your CMS or if the content is cached in Webstudio.

When you configure a Resource, the data is fetched using Cloudflare Workers, which has a built-in [caching system](https://developers.cloudflare.com/workers/reference/how-the-cache-works/).

By default, several factors determine what gets cached and for how long, but one of the primary factors is what the origin system instructs the requesting entity to cache. In other words, the headless CMS tells Cloudflare Workers what can be cached, if anything, and for how long.\
\
If you set a custom **Cache max age** value, it will define your cache lifetime instead of relying on API response headers.

### GraphQL

Choose **GraphQL resource** to fetch data from a GraphQL API. Its editor sends a POST request and provides fields for the GraphQL operation.

There are several fields available to configure the fetch request.

* **URL** – Where the resource is located.
* **Query** – A GraphQL query.
* **GraphQL variables** – An object passed into the query. You can bind it to other variables; for example, use `{ slug: system.params.slug }` on a Dynamic Page. The editor requires a valid object. See [System](#system).
* **Cache max age** and **Headers** – Configure caching and request headers.

The URL accepts a URL or cURL command. Use **Load data** or **Refresh resource data** to inspect the response and request.

{% hint style="success" %}
The requests, including any sensitive secrets like API keys, are handled on the backend and are never exposed to the client.
{% endhint %}

### Assets resource

Choose **Assets resource** to query project assets. Its query editor supports filters, sorting, pagination, and file content. Invalid query settings are shown in the editor. See [Content Engine](/university/foundations/content-engine) for a complete article workflow.

### Sitemap resource

Choose **Sitemap resource** to load the site's static pages. Use it to combine static and dynamic data in a custom sitemap. See the [XML Node component](/university/core-components/xml-node#include-the-static-sitemap).

### Current date resource

Choose **Current date resource** to get the current date normalized to midnight UTC. Format it with the [Time component](/university/core-components/time).

These resource editors provide **Load data** and **Refresh resource data** to update the preview. Sitemap and current date resources need only a name; the Assets resource uses a query editor.

### Email resource

Choose **Email resource** to send a plain-text email when a [Form](/university/core-components/form) is submitted. Choose **Project recipients (or owner)**, **Custom recipients**, or, for a resource within a Form, **Visitor email field**. The visitor option selects a named email input on that Form. Configure **Reply to**, **Subject**, **Body**, and **Attach submitted files**. Custom recipients, Reply to, Subject, and Body can use bindings. The editor validates recipient and sender addresses and expressions.

The **Request** preview shows the email request without sending it. Submit the Form in Preview to see the email service response. See the [Form guide](/university/core-components/form) for delivery behavior and limits.

## Related

* [Expression editor](/university/foundations/expression-editor) – Bind variables to components and create expressions
* [CMS](/university/foundations/cms) – Use Resources to fetch content from external systems
* [Collection](/university/core-components/collection) – Iterate over JSON or Resource data
* [CSS variables](/university/foundations/css-variables) – Define reusable style values (different from data variables)
* [Slot](/university/core-components/slot) – Access global variables in reusable components


# Expression editor

Create logical expressions directly in the UI, enabling conditional display, fallback values, concatenation, and more.

<figure><img src="https://330243581-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTep4EGj6hSC54ozHujyK%2Fuploads%2Fgit-blob-7a7d60e2f4e773b7a3b391d2687676b4ed5bc0cc%2Fwebstudio-expression-editor.png?alt=media" alt="Binding popover with variables and an expression that reads a CMS title"><figcaption></figcaption></figure>

Expression editor is available on every field when clicking the “+” button.

Most commonly used with [Resources](/university/foundations/variables#resource), Expression editor has two primary features:

1. Binding (or “connecting”) external data to Webstudio fields. For example, connecting a blog’s featured image that exists in a headless CMS to the Image component in Webstudio.
2. Running logical expressions such as concatenating two or more values or conditionally displaying a section.

{% hint style="info" %}
Hint: Expand Expression editor to work within a larger window.
{% endhint %}

## Binding

Binding is when you “connect” or “map” various values to fields within Webstudio. For example, when creating a Dynamic Page for a blog, only one page exists in Webstudio, but the values within that page dynamically change based on the URL viewed. The dynamic aspect is enabled by *binding* the various CMS fields to Webstudio components.

You will see [Variables](/university/foundations/variables) within the Expression editor. You can click on the variable or type it in to bind its value to the field. But many times, the value you are looking for is a child within the variable.

Take, for example, the value of a custom Variable called CMS Data:

```json
{
  "title": "Hello World",
  "slug": "hello-world",
  "image": {
    "url": "<https://example.com/image.png>",
    "alt": "I'm an image"
  }
}
```

If you wanted to bind the title of the post to a header component, you need to do more than click on the “CMS Data” Variable. You need to “drill down” or access the children of the variable. This is simply done by typing a `.` after the Variable, which will show the children in the autocomplete (i.e., title, slug, and image). For the title, you would enter `CMS Data.title`, and the value “Hello World” would display. When viewing a different post, that post’s title would display.

For the image URL, you would type `CMS Data.image.url` .

## Expressions

Expression editor supports a simple subset of JavaScript, giving users a simple syntax without the footguns a complex programming language brings.

The following JavaScript expressions are supported:

* [Ternary operator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Conditional_operator) – Useful for conditions such as “If the image is in my CMS, display it, otherwise hide it.” The actual expression for this would look something like `CMS Data.image ? true : false` and be bound to the “Show” field.
* [Template literals](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals) – Useful for inserting dynamic values inside templated text. For example, if you wanted to have “Updated On \<insert dynamic data>”, it would look like `` `Updated On ${CMS Data.updatedOn}` ``. Note the Expression starts and ends with backticks, and the dynamic values are within `${}`.
* [Expressions and operators](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators) – Useful for concatenating two values (alternative solution to template literals). For example, `"Updated on " + CMS Data.updatedOn`.
* Safe string methods: [at](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/at), [split](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/split), [replace](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/replace), [slice](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/slice), [startsWith](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/startsWith), [endsWith](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/endsWith), [includes](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/includes), [toLowerCase](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/toLowerCase), [toUpperCase](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/toUpperCase), [toLocaleLowerCase](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/toLocaleLowerCase), [toLocaleUpperCase](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/toLocaleUpperCase)
* Safe array methods: [at](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/at), [includes](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/includes), [join](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/join), [slice](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/slice)

## Example expressions

### Schema

You can create a schema and bind your [CMS](/university/foundations/cms) data to it. The schema can go in the head or body. Add an [HTML Embed](/university/core-components/html-embed), create a binding, and use the following expression. Be sure to change out the schema type and variables with your data.

```javascript
`<script type="application/ld+json">
    {
  "@context": "https://schema.org",
  "@type": "BlogPosting",
  "headline": "${YOUR.CMS.TITLE}",
  "image": [
    "${YOUR.CMS.LOGO_URL}"
  ],
  "datePublished": "${YOUR.CMS.PUBLISHED_DATE}",
  "dateModified": "${YOUR.CMS.MODIFIED_DATE}",
  "author": [
    {
      "@type": "Person",
      "name": "${YOUR.CMS.AUTHOR_NAME}",
      "url": "${YOUR.CMS.AUTHOR_URL}"
    }
  ]
}
</script>`;
```

### Conditional collection items

Sometimes we need to conditionally hide some records in a [Collection](/university/core-components/collection) based on some context. For example, below a blog post we can have related blog posts, but we wouldn’t want to show the *current* blog post in there. To do so, create an expression on the Show property in Settings.

We need to turn this statement into code: “If the current page’s slug is the same as the current collection’s slug, then don’t show this post.”

Here is the expression. Be sure to modify it with your variables.

```javascript
system.params.slug === collectionItem.slug ? false : true;
```

Again, be sure to change `slug` to your dynamic path parameter and `collectionItem.slug` to your Collection variable and its data.

This expression will now show “false” meaning “turn this off” if the related blog post is actually the same as the current blog post.

## Related

* [Variables](/university/foundations/variables) – Define and use data throughout your pages
* [CMS](/university/foundations/cms) – Connect to external content management systems
* [Dynamic 404 handling](/university/foundations/cms#handling-dynamic-404s) – Return 404 when CMS data is missing on a dynamic page
* [Collection](/university/core-components/collection) – Iterate over data to create dynamic lists
* [HTML Embed](/university/core-components/html-embed) – Embed custom HTML and scripts


# Custom classes & attributes

custom classes, IDs, and data attributes allow you to target elements with custom code, animation libraries, and external scripts.

While [Design tokens](/university/foundations/design-tokens) handle styling in Webstudio, custom classes, IDs, and data attributes serve a different purpose: they allow external code to target specific elements.

{% embed url="<https://www.youtube.com/watch?v=_1QSWHOtk08>" %}

## When to use what

| Feature             | Purpose                                       | Output in HTML                              |
| ------------------- | --------------------------------------------- | ------------------------------------------- |
| **Design tokens**   | Apply and manage styles                       | Converted to optimized classes (atomic CSS) |
| **Custom classes**  | Target elements with custom code              | Yes, exactly as specified                   |
| **Custom IDs**      | Unique element targeting, anchor links        | Yes, exactly as specified                   |
| **Data attributes** | Pass data to JavaScript, custom functionality | Yes, exactly as specified                   |

{% hint style="info" %}
**Important:** Design tokens do NOT output their names as classes in HTML. The token name is an internal reference – the actual CSS output is optimized for performance. Use custom classes when you need a specific class name in the HTML.
{% endhint %}

## Adding custom classes

1. Select an instance in the canvas
2. Open the **Settings** panel (right side)
3. Find the **Class** field
4. Enter your class name(s), separated by spaces

<figure><img src="https://330243581-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTep4EGj6hSC54ozHujyK%2Fuploads%2Fgit-blob-e41f0ff9b5b5fb41a15a4bc2e18cebc9990c6f7a%2Fcustom-attributes.png?alt=media" alt="Settings panel showing Class, ID, and data attribute fields"><figcaption><p>Custom classes, IDs, and data attributes in the Settings panel</p></figcaption></figure>

Multiple classes can be added: `card featured animate-on-scroll`

### Use cases for custom classes

* **Animation libraries**: GSAP, AOS, or other libraries that target elements by class
* **Third-party scripts**: Analytics, heatmaps, or widgets that need class selectors
* **Custom CSS**: When adding CSS via HTML Embed or external stylesheets
* **JavaScript targeting**: `document.querySelectorAll('.my-class')`

## Adding custom IDs

1. Select an instance
2. Open **Settings**
3. Find the **ID** field
4. Enter a unique identifier (no spaces, no `#`)

### Use cases for custom IDs

* **Anchor links**: Link to `#section-name` to scroll to that section
* **JavaScript targeting**: `document.getElementById('my-element')`
* **Form labels**: Connect labels to inputs with matching IDs
* **Table of contents**: Auto-generate TOC based on heading IDs

{% hint style="warning" %}
IDs must be unique on a page. Using the same ID multiple times can cause unexpected behavior.
{% endhint %}

## Adding data attributes

Data attributes let you attach custom data to elements:

1. Select an instance
2. Open **Settings**
3. Scroll to custom properties or use the **+** to add properties
4. Add attributes like `data-speed="0.5"` or `data-section="hero"`

### Use cases for Data attributes

* **Animation parameters**: `data-speed`, `data-delay`, `data-direction`
* **Configuration**: Pass settings to JavaScript components
* **State tracking**: `data-active="true"`, `data-expanded="false"`
* **Analytics**: `data-track="cta-button"`, `data-category="signup"`

## Tokens vs classes: technical details

Design tokens in Webstudio use **atomic CSS** by default, which:

* Generates optimized, single-purpose classes
* Reduces CSS file size by up to 90%
* Improves caching and performance

This means a Token named "card" might output as multiple atomic classes like `a1 b2 c3` rather than a single `.card` class.

**If you need the exact class name in HTML** (for external scripts), use the custom Class field in Settings – not Tokens.

## Example: Integrating with GSAP

To animate elements with GSAP:

1. Add a custom class to target elements: `gsap-fade-in`
2. Add an HTML Embed with your GSAP script:

```html
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.2/gsap.min.js"></script>
<script>
  gsap.from(".gsap-fade-in", {
    opacity: 0,
    y: 50,
    duration: 1,
    stagger: 0.2,
  });
</script>
```

## Example: Scroll spy navigation

Create a scroll spy that highlights the current section:

1. Add IDs to each section: `about`, `services`, `contact`
2. Add `data-nav-item` to corresponding nav links
3. Use JavaScript to detect scroll position and toggle active states

## Related

* [Design tokens](/university/foundations/design-tokens) – For styling elements
* [HTML Embed](/university/core-components/html-embed) – For adding custom scripts
* [Expression editor](/university/foundations/expression-editor) – For dynamic attribute values


# Transforms

CSS Transforms enable you to rotate, scale, skew, and translate elements to create dynamic visual effects.

CSS Transforms allow you to visually manipulate elements by rotating, scaling, skewing, or moving them. They're powerful for creating interactive effects like hover animations and dynamic layouts.

## Accessing Transforms

1. Select an instance in the canvas
2. Open the **Style Panel** on the right
3. Scroll to the **Transforms** section (or search for "transform")
4. Click the **+** button to add a transform

<figure><img src="https://330243581-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTep4EGj6hSC54ozHujyK%2Fuploads%2Fgit-blob-c201a788d662d8ffdb0bc59120b20831498fec22%2Ftransforms-section.png?alt=media" alt="Transforms section in the Style Panel with a rotate transform added"><figcaption><p>The Transforms section in the Style Panel</p></figcaption></figure>

## Transform Properties

### Translate

Move an element horizontally (X) or vertically (Y) without affecting the document flow:

* **translateX**: Move left (negative) or right (positive)
* **translateY**: Move up (negative) or down (positive)
* **translateZ**: Move forward/backward (for 3D effects)

### Rotate

Spin an element around its center point:

* **rotate**: 2D rotation (e.g., `45deg` rotates 45 degrees clockwise)
* **rotateX/Y/Z**: 3D rotation around specific axes

### Scale

Resize an element:

* **scaleX**: Stretch horizontally
* **scaleY**: Stretch vertically
* **scale**: Uniform scaling (1 = original, 2 = double, 0.5 = half)

### Skew

Tilt an element:

* **skewX**: Slant horizontally
* **skewY**: Slant vertically

### Transform Origin

Set the point around which transforms occur (default is center):

* **transform-origin**: e.g., `top left`, `center`, `50% 50%`

## Combining Multiple Transforms

You can stack multiple transforms on a single element. Each transform is applied in order, which affects the final result.

## Common Use Cases

### Card Hover Effect

Create an interactive card that lifts on hover:

1. Add a Box element and style it as a card
2. Add a subtle `box-shadow`
3. Select the **Hover** state from the States dropdown
4. Add transforms:
   * `translateY: -8px` (lift the card)
   * `rotate: 2deg` (slight tilt)
5. Switch back to the default state
6. Add a `transition` property: `transform 0.3s ease`

### Button Scale Effect

Make buttons feel tactile:

1. Select your button
2. In the Hover state, add `scale: 1.05`
3. Add `transition: transform 0.2s ease` on the default state

### Rotating Icons

Create spinning icons for loading states:

1. Select an icon
2. Add `rotate: 360deg` in an animation or hover state

## Transforms with Transitions

Transforms are most effective when combined with [CSS Transitions](/university/foundations/animations#css-transitions) for smooth animations:

1. Apply the **transform** on the target state (hover, focus, etc.)
2. Apply the **transition** on the default state
3. The transition property should include `transform` (e.g., `transition: transform 0.3s ease`)

{% hint style="info" %}
**Tip:** For hover effects, always add the transition on the default state, not the hover state. This ensures smooth animation both on hover and when the cursor leaves.
{% endhint %}

## Z-Index with Transforms

When lifting elements with translateY on hover, you may want them to appear above siblings:

1. Set `position: relative` on the element
2. On hover, increase `z-index` (e.g., `z-index: 10`)

## Performance Considerations

Transforms are GPU-accelerated, making them ideal for animations:

* Prefer `transform: translate()` over `top/left` for movement
* Prefer `transform: scale()` over `width/height` for resizing
* These properties don't trigger layout recalculation, resulting in smoother 60fps animations

## Related

* [Animations](/university/foundations/animations) – Create scroll-driven and interactive animations
* [CSS variables](/university/foundations/css-variables) – Define reusable style values
* [Animation Group](/university/core-components/animation-group) – Container for scroll-driven animations
* [Anatomy of the Webstudio builder](/university/foundations/anatomy-of-the-webstudio-builder) – Learn about the Style Panel


# Animations

Scroll-Driven animations are currently the main focus of Webstudio animations.

The Webstudio Animation Engine is a powerful, performant, and highly customizable system built on the [Web Animations API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Animations_API), with lightweight [polyfills](https://developer.mozilla.org/en-US/docs/Glossary/Polyfill) to ensure broad browser compatibility. It empowers designers and developers to create sophisticated animations with ease, offering scalability and flexibility for projects of any complexity. Whether you're animating simple transitions or orchestrating intricate, multi-layered effects, this engine provides the tools to bring your vision to life.\
\
If you want to learn the lower-level technical foundation behind Scroll-Driven animations, check out <https://scroll-driven-animations.style/>.

## Core Features

* **Extreme Performance** – Optimized for smooth playback, even with complex animations and large-scale projects.
* **Scalability** – Seamlessly handles everything from single-element transitions to nested, multi-group sequences.
* **Full Customization** – Animate any CSS property and stack unlimited Animation Groups for intricate effects.
* **Cross-Browser Support** – Built on the Web Animations API with polyfills to ensure consistent behavior across browsers.

## Key Components

The engine’s user interface is composed of three main components, each designed to work together for a cohesive animation workflow:

1. [**Animation Group**](/university/core-components/animation-group) – The cornerstone of the system, Animation Groups serve as containers that define how their contents animate. They support two trigger types—view-based (scrollport entry/exit) and scroll-based (scroll position)—and offer extensive configuration options like axis, scroll source, insets, and debug tools. You can nest Animation Groups infinitely to craft complex, layered animations.
2. [**Text Animation**](/university/core-components/text-animation) – Simplifies animating text by automatically splitting it into individual words or characters, allowing for dynamic effects without manual wrapping.
3. [**Video Animation**](/university/core-components/video-animation) - allows video playback when video enters the scrollport.
4. [**Stagger Animation**](/university/core-components/stagger-animation) – Creates a cascading effect by animating child elements sequentially.

## Animation Tutorials

Learn the fundamentals and advanced techniques:

| Tutorial                                                                     | Description                                  |
| ---------------------------------------------------------------------------- | -------------------------------------------- |
| [Scroll Animations 101](https://www.youtube.com/watch?v=vleipSDU_Xo)         | Introduction to scroll-driven animations     |
| [Parallax Animation 101](https://www.youtube.com/watch?v=Hzdnlz67nvQ)        | Create depth with parallax scrolling effects |
| [Fade in Animation 101](https://www.youtube.com/watch?v=ZDxwKJ_yh0A)         | Basic fade-in effects on scroll              |
| [Hero Page Load Animation](https://www.youtube.com/watch?v=RDQLbUqarUM)      | Animate hero sections on page load           |
| [Stagger Animations](https://www.youtube.com/watch?v=8y2n8qEkv94)            | Create cascading animation sequences         |
| [Animation Subject Options](https://www.youtube.com/watch?v=QQLNIeUQxPI)     | Understanding animation subjects and options |
| [Advanced Scroll Animation](https://www.youtube.com/watch?v=h8dhdb6bmWw)     | Complex scroll-driven animation techniques   |
| [Scroll-Driven Text Animations](https://www.youtube.com/watch?v=UM8WNeqWWqM) | Animate text as users scroll                 |
| [Perfecting Animation Ranges](https://www.youtube.com/watch?v=c_ObDvsYOnk)   | Fine-tune start/end points                   |

## Hover Animations

For interactive effects like hover animations, Webstudio leverages [CSS variables](/university/foundations/css-variables) with a "parent interaction modifies children" approach. Define hover states on a parent element, then use CSS variables to adjust child element properties dynamically. For more information, see [CSS variables – Parent-child interactions](/university/foundations/css-variables#parent-child-interactions).

## CSS Transitions

For simple state-based animations (hover effects, button interactions), use CSS Transitions in the Style Panel's Advanced section.

1. Select an instance
2. Open the **Advanced** section in the Style Panel
3. Add a `transition` property with values like `all 0.3s ease` or target specific properties like `background-color 0.2s, transform 0.3s`
4. Define the hover/focus states with changed values
5. The browser will animate smoothly between states

CSS Transitions work best for:

* Button hover effects
* Link underlines
* Color changes
* Simple transforms (scale, rotate)

For complex, multi-step animations or scroll-driven effects, use the Animation Group component instead.

***

The Webstudio Animation Engine is designed to make animations accessible yet powerful, bridging the gap between simplicity and sophistication. Whether you’re building subtle scroll-driven effects or elaborate interactive sequences, it delivers the performance and versatility to match your ambition.

## Related

* [Scroll-Driven Animations](https://webstudio.is/scroll-driven-animations) – Learn more about scroll-driven animations in Webstudio
* [Animation Group](/university/core-components/animation-group) – Container for scroll-driven animations
* [Text Animation](/university/core-components/text-animation) – Animate text by words or characters
* [Stagger Animation](/university/core-components/stagger-animation) – Create cascading animation effects
* [Video Animation](/university/core-components/video-animation) – Control video playback on scroll
* [Transforms](/university/foundations/transforms) – Rotate, scale, skew, and translate elements


# CMS

Connect to your existing CMS or the one that works best for you.

Webstudio is backend-agnostic, meaning it enables you to connect to any backend as long as it provides an HTTP API (see [compatible CMSs and features](#compatible-cmss) for more info).

While some users may be used to seeing a CMS tab within their platform, Webstudio is different. It’s approached CMS by providing a flexible way to interact with third-party systems such as CMSs, CRMs, and databases.

{% hint style="info" %}
Need help finding a CMS? Use the [Headless CMS Finder](https://wstd.us/cms-finder).
{% endhint %}

The building blocks of Webstudio CMS are:

1. [**Dynamic pages**](#dynamic-pages) – In their simplest form, they are essentially blog templates – one page that dynamically displays data depending on the URL viewed.
2. [**Resources**](/university/foundations/variables#resource) – A way to fetch data from an API, whether it's a simple blog post or a complex data model.
3. [**Bindings**](/university/foundations/expression-editor#binding) – Enabling connecting or mapping the CMS data to Webstudio components. You can bind external data to any component and field within Webstudio, from rich text to meta titles.

{% tabs %}
{% tab title="CMS 101" %}
3 minutes to get you acclimated with how CMS works in Webstudio.

{% embed url="<https://youtu.be/H_5IXjJeLvs>" fullWidth="true" %}
{% endtab %}

{% tab title="CMS Full Tutorial" %}
A 20-minute full tutorial on setting up a CMS integration.

{% embed url="<https://youtu.be/QC6Y7BHduLw>" %}
{% endtab %}

{% tab title="CMS Playlist" %}
A collection of all our CMS content on YouTube.

{% embed url="<https://www.youtube.com/playlist?list=PL4vVqpngzeT6zVt2Jrsx3v1e0z3gq15O4>" %}
{% endtab %}
{% endtabs %}

## Dynamic Pages

Similar to a static page, the path includes dynamic parameters, like a post slug. This enables the page contents to change dynamically based on the requested page.

Adding parameters to a page path will automatically turn the page into a Dynamic Page.

Use a [path pattern](/university/foundations/path-patterns) to define which URLs use the Dynamic Page. For example, `/post/:slug` provides the value of `slug` from the URL.

The value of the parameter comes from whatever is in the URL. If the path is `/post/:slug` and somebody views `/post/hello-world` then `hello-world` is the value used in your Resource.

### **Address Bar**

The Address Bar enables previewing Dynamic Pages in the editor by entering parameter value(s).

For the Dynamic Path `/post/:slug`, you would enter a slug value that exists in the CMS, such as `hello-world`.

<figure><img src="https://330243581-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTep4EGj6hSC54ozHujyK%2Fuploads%2Fgit-blob-fa5f5c364c16ae6f4e61597ac4f8e2064c359a15%2Faddress-bar.png?alt=media" alt="Address bar with hello-world entered as the dynamic slug preview value"><figcaption><p>Entering hello-world as a test value</p></figcaption></figure>

{% hint style="danger" %}
The static part of the URL (in this case, `/post/`) is already in the Address Bar and should not be included when adding the test value.
{% endhint %}

Address Bar values are saved in the editor by:

* Manually entering values
* Navigating links that lead to the Dynamic Page, such as clicking on `/post/hello-world`

## Resources

A Resource variable gets its value from a fetch request, allowing data from a remote system to be used within Webstudio.

In the context of Dynamic Pages, Resources are used to fetch specific information determined by the URL, or more specifically, the value of the parameter(s) in the URL.

For example, the Resource will dynamically fetch posts from the CMS whose slug value equals the slug being viewed. The query may look something like this `posts(slug: system.params.slug)`, and when `/post/hello-world` is viewed, the query will translate to `posts(slug: "hello-world")` (i.e., “get posts where the slug value is “hello world”).

Dynamic pages may have multiple parameters, enabling the query to become even more dynamic. For example, in addition to `:slug`, you can also add `:lang` to fetch and display localized content.

If interacting with a GraphQL API, use the [GraphQL resource](/university/foundations/variables#graphql).

## Binding Data

At this point, the Resource Variable has the CMS Data, and now you need to bind or connect that data to the components and fields.

Binding data is done with the [Expression editor](/university/foundations/expression-editor).

For example, go to a Header component > Settings > Text Content > and the “+” button. Within the Expression editor, add the Resource Variable that contains the CMS data and the title value within it. This may look like `CMS Data.title`.

For more information, see the [Expression editor documentation](/university/foundations/expression-editor).

## Compatible CMSs

Webstudio CMS supports any content management system (CMS) that provides an HTTP API. This feature enables users to use their existing CMS or the one that works best for them.

Below is a non-comprehensive list of CMSs, whether or not they provide an HTTP API (i.e., compatible with Webstudio), and the compatibility of specific CMS features. You can also use the [Headless CMS Finder](https://wstd.us/cms-finder) to compare and filter headless CMS options based on key features and pricing.

| CMS                                       | Compatible | Rich text                                                                                                                                                                                        | Tutorial                                               | Template                                       | Known issues                                                             |
| ----------------------------------------- | ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------ | ---------------------------------------------- | ------------------------------------------------------------------------ |
| [Hygraph](https://hygraph.com/)           | ✅          | ✅ Must request HTML                                                                                                                                                                              | [Tutorial](/university/integrations/hygraph)           | [Template](https://wstd.us/hygraph-template)   |                                                                          |
| [Sanity](https://www.sanity.io/)          | ✅          | ❌                                                                                                                                                                                                |                                                        |                                                |                                                                          |
| [Strapi](https://strapi.io/)              | ✅          | ✅ Must use the [CKEditor 5 integration](https://market.strapi.io/plugins/@ckeditor-strapi-plugin-ckeditor) and field as it outputs HTML. Not compatible with the default rich text field.        |                                                        |                                                |                                                                          |
| [Contentful](https://www.contentful.com/) | ✅          | ✅ Using Markdown field                                                                                                                                                                           |                                                        |                                                |                                                                          |
| [WordPress](https://wordpress.org/)       | ✅          | ✅                                                                                                                                                                                                | [Tutorial](/university/integrations/wordpress)         | [Template](https://wstd.us/wordpress-template) |                                                                          |
| [Drupal](https://www.drupal.org/)         | ✅          | ✅                                                                                                                                                                                                |                                                        |                                                |                                                                          |
| [Directus](https://directus.io/)          | ✅          | ✅                                                                                                                                                                                                |                                                        |                                                |                                                                          |
| [Hashnode](https://hashnode.com/headless) | ✅          | ✅                                                                                                                                                                                                |                                                        |                                                |                                                                          |
| [Payload](https://payloadcms.com/)        | ✅          | ✅ Need to create a field and hook that auto converts rich text from field A and saves HTML in field B ([src](https://payloadcms.com/docs/rich-text/lexical#outputting-html-from-the-collection)) |                                                        |                                                |                                                                          |
| [Airtable](https://www.airtable.com/)     | ✅          | ✅                                                                                                                                                                                                | [Tutorial](/university/integrations/airtable-frontend) | [Template](https://wstd.us/airtable-template)  |                                                                          |
| [Baserow](https://baserow.io/)            | ✅          | ✅                                                                                                                                                                                                |                                                        |                                                |                                                                          |
| [Notion](https://www.notion.so/)          | ✅          | ❌                                                                                                                                                                                                | [Tutorial](/university/integrations/notion)            | [Template](https://wstd.us/notion-template)    | [Can't use pages](https://github.com/webstudio-is/webstudio/issues/3709) |
| [Flotiq](https://flotiq.com/)             | ✅          | ✅                                                                                                                                                                                                | [Tutorial](/university/integrations/flotiq)            |                                                |                                                                          |
| [Ghost](https://ghost.org/)               | ✅          | ✅                                                                                                                                                                                                |                                                        | [Template](https://wstd.us/ghost-template)     |                                                                          |
| [Coda](https://coda.io/)                  | ✅          | ✅                                                                                                                                                                                                |                                                        |                                                | [Multiple](https://github.com/webstudio-is/webstudio/issues/3708)        |
| [Hyvor Blogs](https://blogs.hyvor.com/)   | ✅          | ✅                                                                                                                                                                                                |                                                        | [Template](https://wstd.us/template-hyvor)     |                                                                          |

{% hint style="warning" %}
While we do our best to verify the supported features, we may make mistakes in our research.
{% endhint %}

### Rich text support

While most CMS field types seamlessly map to Webstudio components (e.g., Plain Text → Heading), rich text may not, depending on how the CMS stores/delivers it.

Webstudio renders rich text delivered as HTML or Markdown. If a CMS returns a proprietary AST or another structured rich-text format, convert it to HTML or Markdown before binding it in Webstudio. This conversion can happen in the CMS, its API layer, or a proxy.

Rich text in the form of HTML can be bound to the [Content Embed Component](/university/core-components/content-embed), and Markdown can be bound to the [Markdown Embed Component](/university/core-components/markdown-embed).

## Handling dynamic 404s

On a dynamic page, the URL may technically exist (e.g. `/post/hello-world`) but the Resource query returns no data — for example, because the slug doesn't match any record. In that case the page should return `404` instead of rendering empty content.

There are three steps to handle this correctly.

### 1. Set the status code

Open **Page Settings > Status Code** and bind an expression to it. The goal is to look for some piece of data in the response and if it's not there, output `404`:

```javascript
cmsData.data[0].id ? 200 : 404;
```

This example looks for the ID of a record. If it's there, output `200` (found!) otherwise `404` (not found).

<figure><img src="https://330243581-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTep4EGj6hSC54ozHujyK%2Fuploads%2Fgit-blob-cd1d92e2ac9ec0390ceb84019835d63a89b63f93%2Fcms-dynamic-404-status-code.png?alt=media" alt="Status code field in Page settings with an expression bound"><figcaption><p>Binding an expression to the status code</p></figcaption></figure>

{% hint style="info" %}
The exact key to look for will depend on your CMS, but think of something that will always be there if the post/record is found (slug, ID, title).
{% endhint %}

### 2. Show 404 content conditionally

Add a component (e.g. a Box) to the page that contains your 404 message. Set its **Show** condition to the same expression:

```javascript
!cmsData.data[0].id;
```

When this evaluates to `true`, the 404 content is shown.

<figure><img src="https://330243581-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTep4EGj6hSC54ozHujyK%2Fuploads%2Fgit-blob-0be125f61400b5c8c52abc29314d9883b201e161%2Fcms-dynamic-404-show-404-content.png?alt=media" alt="Box component with a Show condition for missing CMS data"><figcaption><p>Setting the Show condition on the 404 content</p></figcaption></figure>

{% hint style="info" %}
To reuse an existing custom 404 page design without rebuilding it, [add a Slot](/university/core-components/slot) and select your 404 page's content as the slot source. This keeps the 404 UI in one place and lets you reuse it across any dynamic page.
{% endhint %}

### 3. Hide regular content conditionally

Select the component (e.g. a Box) that wraps the normal page content and set its **Show** condition to the same expression:

```javascript
cmsData.data[0].id;
```

This hides the regular content when there is no data, avoiding an empty-looking page.

<figure><img src="https://330243581-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTep4EGj6hSC54ozHujyK%2Fuploads%2Fgit-blob-f411c61319f07db9c6de46e5564e72e28ce82305%2Fcms-dynamic-404-show-regular-content.png?alt=media" alt="Box component with a Show condition for available CMS data"><figcaption><p>Setting the Show condition on regular content</p></figcaption></figure>

## Alternative: redirect instead of showing 404 content

Instead of rendering 404 content on the same page, you can redirect the user to another page entirely — for example your custom `/404` page — using **Page Settings > Redirect** bound to an expression:

```javascript
!cmsData.data[0].id ? "/404" : ""
```

When data is missing, this redirects to `/404`. When data is found, the empty string means no redirect occurs and the page loads normally.

This approach is simpler — no need to conditionally show/hide content — but the user sees a URL change rather than staying on the original URL.

## Related

* [Webstudio CMS](https://webstudio.is/cms) – Learn more about CMS capabilities in Webstudio
* [Headless CMS Finder](https://webstudio.is/tools/headless-cms-finder) – Compare and find the right headless CMS
* [Variables](/university/foundations/variables) – Define Resources to fetch CMS data
* [Expression editor](/university/foundations/expression-editor) – Bind CMS data to components
* [Collection](/university/core-components/collection) – Iterate over CMS data to create lists
* [Content Embed](/university/core-components/content-embed) – Display rich text HTML from your CMS
* [Markdown Embed](/university/core-components/markdown-embed) – Display Markdown content from your CMS
* [Custom 404 page](/university/how-tos/how-to-make-a-custom-404-page) – Create a custom 404 design to reuse on dynamic pages


# Reusability & maintainability

Webstudio provides several tools that let you build sites that are easy to update and scale. Instead of making the same change in dozens of places, you can centralize content, styles, and structure so that editing one thing updates everywhere.

## Slots — shared structure across pages

<figure><img src="https://330243581-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTep4EGj6hSC54ozHujyK%2Fuploads%2Fgit-blob-b18897c33d415ad3dc6b4049837259aa583e444a%2Fslot-component-overview.png?alt=media&amp;token=97c90420-4330-4f3d-838c-b7a8646f81c2" alt="Slot component in the Navigator showing shared structure"><figcaption><p>A Slot references another page's content — edit the source once, all slots update</p></figcaption></figure>

A [Slot](/university/core-components/slot) is a component that references the content of another page. Any instance that uses that slot always renders the same content, so editing the source page updates every slot automatically.

**Common uses:**

* Navigation header and footer shared across all pages
* A cookie banner or chat widget added once and reused everywhere
* A 404 layout reused inside multiple dynamic page templates

To create a shared layout, build the header (or any repeating section) on one page, then add a Slot on every other page and point it at that source. You only maintain the design in one place.

{% hint style="info" %}
[CSS variables](/university/foundations/css-variables) defined on a parent are accessible inside Slots. [Data variables](/university/foundations/variables), however, are scoped to their page and are not available inside Slots.
{% endhint %}

→ [Slot component reference](/university/core-components/slot)

## Design tokens & CSS variables — reusable styles

<figure><img src="https://330243581-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTep4EGj6hSC54ozHujyK%2Fuploads%2Fgit-blob-aaa05f1bc5bce4d2ca0c84c000c2a41ec9ce7fec%2Fh1-token.png?alt=media&amp;token=bf16ddae-81e7-4109-8938-9341906af6b3" alt="A design token applied to a Heading instance in the Style Sources input"><figcaption><p>A token applied to an instance — styles update everywhere this token is used</p></figcaption></figure>

CSS variables and design tokens work together as two layers of reusability.

[**CSS variables**](/university/foundations/css-variables) are the bottom layer — individual named values like colors, sizes, and spacing. Define `--color-brand` once, and use it in any style input across the entire site. When the brand color changes, update the variable in one place and every element referencing it updates.

<figure><img src="https://330243581-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTep4EGj6hSC54ozHujyK%2Fuploads%2Fgit-blob-aedd142a7c6b54e981a381ead6e66765ae1840fb%2Fcss-var-example.png?alt=media&amp;token=c9128478-d772-476e-b371-289d596361be" alt="CSS variable defined in the Advanced section with a name and value" width="319"><figcaption><p>Define a CSS variable once, use it anywhere</p></figcaption></figure>

[**Design tokens**](/university/foundations/design-tokens) are the next layer — named collections of styles that can be applied to any element, similar to CSS classes but without their common problems like combo classes, breakpoint conflicts, and accidental style inheritance. A `card` token might define padding, background, and border-radius. The values inside a token can reference CSS variables, giving you another level of reuse.

Together: CSS variables store the raw values, tokens package those variables into semantic, reusable style groups.

When you update a token — say, changing the padding defined on it — every element using that token updates automatically. Without tokens, you would make the same style change on every element individually.

**Common uses:**

* CSS variables: `--color-brand`, `--space-md`, `--radius-lg`
* Tokens: `button-primary`, `card`, `heading-lg` — each referencing those variables internally

→ [Design tokens](/university/foundations/design-tokens) · [CSS variables](/university/foundations/css-variables)

## Dynamic data — one template, many pages

### Static pages vs. dynamic pages

A **static page** is the full implementation of a design — layout, styles, and content all built directly on the canvas. The content is part of the page itself: you type the text, drop in the images, and publish. A home page, an about page, or a contact page are typically static.

A **dynamic page** is the design without the content. It has the same layout and structure as a static page, but instead of real text and images, its content comes from data — fetched at request time from an external source. The same template renders differently for every record: one URL loads a blog post, another loads a different one, using the exact same page design.

This is the key to scaling content-heavy sites. Instead of creating a separate page for every blog post, product, or team member, you build one dynamic page template and let the data do the rest.

<figure><img src="https://330243581-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTep4EGj6hSC54ozHujyK%2Fuploads%2Fgit-blob-e4534e5f14e219c0cc72bae7edbd8c2a0edad181%2Fright-and-wrong-way-collections.png?alt=media&amp;token=f5ede0b3-3120-476b-818b-e95bd140d5e0" alt="Comparison showing one template for all records vs. one page per record"><figcaption><p>One template renders all records — don't create a separate page for each</p></figcaption></figure>

→ [CMS & dynamic data](/university/foundations/cms)

## Page templates — repeatable page creation

[Page templates](/university/foundations/page-templates) let designers create reusable page blueprints inside a project. Unlike dynamic pages, which render many URLs from one live page and external data, page templates create independent regular pages.

Use page templates for repeatable static page types, such as service pages, campaign pages, landing pages, or client-created content pages that should start from a controlled structure.

## Why Webstudio is not a CMS

Webstudio is a **visual builder**, not a content management system. It does not have a built-in database for storing and managing hundreds of records.

This means:

* Creating one page per blog post is **not the right approach** for content-heavy sites. You would end up managing hundreds of pages inside the builder, with no structured editing workflow, no content relations, and no bulk operations.
* The right approach is to store that content in a dedicated CMS (WordPress, Directus, Baserow, Ghost, etc.) and connect it to a single dynamic page template in Webstudio via the [CMS integration](/university/foundations/cms).

The builder is designed to be the **presentation layer** — responsible for layout, design, and how data is displayed — while the CMS is the **data layer** responsible for storing and editing content at scale.

{% hint style="info" %}
Webstudio dynamic pages use **one template for all records**. The CMS handles creating, editing, and organizing the actual records. Webstudio fetches and displays them.
{% endhint %}

## Summary

| Tool                                                                                                            | What it solves                                                  |
| --------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------- |
| [Slots](/university/core-components/slot)                                                                       | Duplicate structure across pages — edit once, update everywhere |
| [CSS variables](/university/foundations/css-variables) + [Design tokens](/university/foundations/design-tokens) | Hard-coded style values scattered across elements               |
| [Dynamic data](/university/foundations/cms)                                                                     | Building one page per content item instead of one template      |
| [Page templates](/university/foundations/page-templates)                                                        | Recreating the same page structure manually                     |
| External CMS                                                                                                    | Managing dozens or hundreds of records inside the builder       |

## Related

* [Slot](/university/core-components/slot) – Shared components across pages
* [CSS variables](/university/foundations/css-variables) – Individual named values (colors, sizes, spacing)
* [Design tokens](/university/foundations/design-tokens) – Reusable style collections built on top of CSS variables
* [CMS & dynamic data](/university/foundations/cms) – One template, many pages
* [Page templates](/university/foundations/page-templates) – Reusable blueprints for creating pages
* [Variables](/university/foundations/variables) – Binding data to the canvas


# Copy-Paste

Webstudio supports copy-pasting within the platform and from external sources.

Cut removes selected content only after it is copied to the clipboard. If project data changes while the cut is pending, Webstudio keeps the copy but removes nothing. A message asks you to try cutting again.

{% content-ref url="/pages/gpF7j4xSpL3Rf3j8uKiR" %}
[CSS](/university/foundations/copy-paste/css)
{% endcontent-ref %}

{% content-ref url="/pages/NQakCzy6YxVpC7thH065" %}
[HTML with CSS](/university/foundations/copy-paste/html-with-css)
{% endcontent-ref %}

{% content-ref url="/pages/EAQzIEHMn5DFGmayQNbU" %}
[HTML with Tailwind](/university/foundations/copy-paste/html-with-tailwind)
{% endcontent-ref %}

{% content-ref url="/pages/Dr0lYHGPyBgrdNEeFvgW" %}
[Markdown](/university/foundations/copy-paste/markdown)
{% endcontent-ref %}

{% content-ref url="/pages/sv5Bkjd5wjEKzqtQ8kvd" %}
[Webflow](/university/foundations/copy-paste/webflow)
{% endcontent-ref %}

{% content-ref url="/pages/NngIcOJf3CW992YNz4Sy" %}
[SVG](/university/foundations/copy-paste/svg)
{% endcontent-ref %}

{% content-ref url="/pages/wOMjwstO9dAIN6WF6SNu" %}
[Referenced Images](/university/foundations/copy-paste/images)
{% endcontent-ref %}


# CSS

Paste CSS into Webstudio, and it'll be translated to the various fields in the Style Panel.

[CSS](https://developer.mozilla.org/en-US/docs/Web/CSS) is the design language of the internet. With this feature, you can copy CSS from anywhere and paste it into Webstudio, which will parse it and populate the various fields in the Style Panel.

## How to paste CSS

<figure><img src="https://330243581-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTep4EGj6hSC54ozHujyK%2Fuploads%2Fgit-blob-c72eda4521be3f39de23a978c4f55bb8d2b1d472%2Fcss-paste.gif?alt=media" alt="pasting css from Figma into Webstudio"><figcaption></figcaption></figure>

1. Copy CSS declaration(s) such as `background: blue;`.
2. In Webstudio, go to the Style Panel > Advanced > and click "+".
3. Paste the CSS and press enter.

That's it.

The styles are parsed and displayed in their respective fields (they will also be shown in the Advanced section).

## Use cases

1. **Design tools like** [**Figma**](https://figma.com/) **and** [**Penpot**](https://penpot.app/) provide CSS, making copying and pasting individual layer styles into Webstudio easier.
2. **Third-party libraries like** [**Open Props**](https://open-props.style/) provide expertly crafted CSS variables, allowing you to import them via paste.
3. **Custom stylesheets** containing Design Systems and other declarations can be imported.
4. Inspect your website with [**DevTools**](https://developer.chrome.com/docs/devtools) and copy the CSS.
5. Online tutorials, [CodePen](https://codepen.io/), ChatGPT, and many other sources provide CSS.

## Pasting CSS declarations

When pasting into the Style Panel, Webstudio accepts CSS declarations (e.g., `background: blue;`). Selectors are not needed because Webstudio applies styles directly to the selected instance.

## Related

* [HTML with CSS](/university/foundations/copy-paste/html-with-css) – Paste HTML containing `<style>` blocks
* [HTML with Tailwind](/university/foundations/copy-paste/html-with-tailwind) – Paste HTML containing Tailwind utility classes
* [Markdown](/university/foundations/copy-paste/markdown) – Paste Markdown to create components automatically
* [Webflow](/university/foundations/copy-paste/webflow) – Copy Webflow components into Webstudio
* [Shortcuts](/university/foundations/shortcuts) – Keyboard shortcuts for faster workflows
* [Commands and search](/university/foundations/commands-and-search) – Quick access to commands and settings
* [Anatomy of the Webstudio builder](/university/foundations/anatomy-of-the-webstudio-builder) – Overview of the Webstudio interface


# HTML with CSS

Paste HTML with style blocks and convert CSS classes into Webstudio styles.

When you paste HTML that includes `<style>` blocks, Webstudio extracts the CSS rules and converts class-based selectors into [design tokens](/university/foundations/design-tokens). Use this when you want pasted markup to become editable Webstudio components instead of remaining embedded code.

## How to paste HTML with CSS

1. Copy HTML that includes a `<style>` block.
2. Paste it onto the canvas in Webstudio.
3. Webstudio creates the component structure and applies matching class styles as reusable style tokens.

If a nested selector references elements not present in the pasted HTML, Webstudio shows a notification listing the skipped selectors.

If the pasted HTML/CSS references image URLs, such as `<img src="...">` or `background-image: url(...)`, Webstudio uploads those images into [Assets](/university/foundations/assets) and rewrites the pasted components/styles to use the uploaded assets.

## Related

* [CSS](/university/foundations/copy-paste/css) – Paste CSS declarations into the Style Panel
* [HTML with Tailwind](/university/foundations/copy-paste/html-with-tailwind) – Paste HTML containing Tailwind utility classes
* [Referenced images](/university/foundations/copy-paste/images) – Understand image handling during paste
* [Design tokens](/university/foundations/design-tokens) – Learn how reusable style packages work
* [HTML Embed](/university/core-components/html-embed) – Embed custom HTML when you do not need native Webstudio components


# HTML with Tailwind

Paste HTML with Tailwind CSS classes and convert utilities into Webstudio styles.

You can paste HTML with Tailwind CSS classes into Webstudio and convert the utility classes into native Webstudio styles.

For general Tailwind HTML, use the **Paste HTML with Tailwind classes** command:

1. Copy HTML containing Tailwind classes, such as `<div class="flex items-center gap-4 p-6 bg-white rounded-lg">`
2. Open [Commands & search](/university/foundations/commands-and-search) with `⌘ + K` (`Ctrl + K` on Windows)
3. Search for **Paste HTML with Tailwind classes**
4. Run the command

<figure><img src="https://330243581-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTep4EGj6hSC54ozHujyK%2Fuploads%2Fgit-blob-78b6d1d1deed97dbcb75cb1decc945d3becada13%2Fpaste-html-tailwind-command.png?alt=media" alt="Commands and search showing the Paste HTML with Tailwind classes command"><figcaption><p>Paste HTML with Tailwind classes command</p></figcaption></figure>

HTML/Tailwind output copied from [Inception](/university/inception#copy-htmltailwind) can also be pasted directly into the Builder.

If the pasted HTML references image URLs, Webstudio uploads those images into [Assets](/university/foundations/assets) and rewrites the pasted image instances to use the uploaded assets.

## Related

* [HTML with CSS](/university/foundations/copy-paste/html-with-css) – Paste HTML containing `<style>` blocks
* [Referenced images](/university/foundations/copy-paste/images) – Understand image handling during paste
* [Commands & search](/university/foundations/commands-and-search) – Learn how to run commands from the keyboard
* [Inception](/university/inception#copy-htmltailwind) – Copy HTML/Tailwind output from Inception


# Markdown

Pasting Markdown automatically adds the respective components.

Copy Markdown and paste it into Webstudio, and it will automatically add all the components, such as headings, images, and links.

{% hint style="info" %}
Many tools use Markdown behind the scenes, letting you copy directly from the editor.
{% endhint %}

Example sources and Markdown flavors:

* Standard Markdown
* GitHub-flavored Markdown
* Notion
* SurferSEO
* Obsidian
* Coda

If the pasted Markdown references images, Webstudio uploads those images into [Assets](/university/foundations/assets) and rewrites the generated image instances to use the uploaded assets.

Want to embed Markdown? See [Markdown Embed](/university/core-components/markdown-embed).

## Related

* [CSS](/university/foundations/copy-paste/css) – Paste CSS to populate the Style Panel
* [Referenced images](/university/foundations/copy-paste/images) – Understand image handling during paste
* [Webflow](/university/foundations/copy-paste/webflow) – Copy Webflow components into Webstudio
* [Shortcuts](/university/foundations/shortcuts) – Keyboard shortcuts for faster workflows
* [Commands and search](/university/foundations/commands-and-search) – Quick access to commands and settings
* [Anatomy of the Webstudio builder](/university/foundations/anatomy-of-the-webstudio-builder) – Overview of the Webstudio interface


# Webflow

Webstudio supports pasting Webflow Elements, converting them from Webflow format to Webstudio format.

It works by copying anything in Webflow format, such as component libraries and projects, and pasting it into Webstudio, transferring the structure and styles.

If the pasted Webflow content references images, including background images, Webstudio uploads those images into [Assets](/university/foundations/assets) and rewrites the pasted components/styles to use the uploaded assets.

## What does and doesn’t transfer

### Elements

| Webflow Element  | Transfers? | Notes                                                                                                                                                                                                                  |
| ---------------- | ---------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Section          | ✅          |                                                                                                                                                                                                                        |
| Container        | ✅          |                                                                                                                                                                                                                        |
| Quick Stack      | ✅          |                                                                                                                                                                                                                        |
| V Flex           | ✅          |                                                                                                                                                                                                                        |
| H Flex           | ✅          |                                                                                                                                                                                                                        |
| Div Block        | ✅          |                                                                                                                                                                                                                        |
| List             | ✅          |                                                                                                                                                                                                                        |
| List Item        | ✅          |                                                                                                                                                                                                                        |
| Link Block       | ✅          |                                                                                                                                                                                                                        |
| Button           | ✅          | Maps to "[Link](/university/core-components/link)". [Buttons](/university/core-components/button) are for interactions, like submitting a form, not linking.                                                           |
| Heading          | ✅          |                                                                                                                                                                                                                        |
| Paragraph        | ✅          |                                                                                                                                                                                                                        |
| Text Link        | ✅          |                                                                                                                                                                                                                        |
| Text Block       | ✅          |                                                                                                                                                                                                                        |
| Block Quote      | ✅          |                                                                                                                                                                                                                        |
| Rich Text        | ✅          | There is no Rich Text component in Webstudio, though any children of the copied Rich Text paste in as their respective components. For example, Rich Text with a Heading and Link will transfer as a Heading and Link. |
| Collection List  | ❌          | Though Webstudio does have a "[Collection](/university/core-components/collection)" component.                                                                                                                         |
| Image            | ✅          |                                                                                                                                                                                                                        |
| Video            | ❌          | Can be rebuilt “[Vimeo](/university/core-components/vimeo)” or “[HTML Embed](/university/core-components/html-embed)”                                                                                                  |
| YouTube          | ❌          | Can be rebuilt using “[HTML Embed](/university/core-components/html-embed)”. [See status](https://github.com/webstudio-is/webstudio/issues/1747) of this component in Webstudio.                                       |
| Lottie Animation | ❌          | Can be rebuilt using “[HTML Embed](/university/core-components/html-embed)”                                                                                                                                            |
| Spline Scene     | ❌          | Can be rebuilt using “[HTML Embed](/university/core-components/html-embed)”                                                                                                                                            |
| Form Block       | ✅          |                                                                                                                                                                                                                        |
| Label            | ✅          |                                                                                                                                                                                                                        |
| Input            | ✅          |                                                                                                                                                                                                                        |
| File Upload      | ❌          | [See status](https://github.com/webstudio-is/webstudio/issues/3023) of this component in Webstudio                                                                                                                     |
| Text Area        | ✅          |                                                                                                                                                                                                                        |
| Checkbox         | ✅          |                                                                                                                                                                                                                        |
| Radio Button     | ✅          |                                                                                                                                                                                                                        |
| Select           | ✅          |                                                                                                                                                                                                                        |
| reCAPTCHA        | ❌          | reCAPTCHA doesn’t transfer as Webstudio uses alternative methods of preventing spam.                                                                                                                                   |
| Form Button      | ✅          | Maps to "[Button](/university/core-components/button)"                                                                                                                                                                 |
| Search           | ❌          |                                                                                                                                                                                                                        |
| Background Video | ❌          |                                                                                                                                                                                                                        |
| Dropdown         | ❌          | Can be rebuilt using Radix “[Select](/university/radix/select)”                                                                                                                                                        |
| Code Embed       | ✅          |                                                                                                                                                                                                                        |
| Lightbox         | ❌          |                                                                                                                                                                                                                        |
| Locales List     | ❌          |                                                                                                                                                                                                                        |
| Navbar           | ✅          | Generates the corresponding components such as [Boxes](/university/core-components/element) and [Links](/university/core-components/link) with [Tokens](/university/foundations/design-tokens) and styles.             |
| Slider           | ❌          | Can be rebuilt using Swiper.js in the Marketplace                                                                                                                                                                      |
| Tabs             | ❌          | Can be rebuilt using Radix “[Tabs](/university/radix/tabs)”                                                                                                                                                            |
| Map              | ❌          |                                                                                                                                                                                                                        |
| Facebook         | ❌          |                                                                                                                                                                                                                        |
| X (Twitter)      | ❌          |                                                                                                                                                                                                                        |
| Custom Element   | ❌          | [See status](https://github.com/webstudio-is/webstudio/issues/3632) of this component in Webstudio                                                                                                                     |
| Code Block       | ✅          |                                                                                                                                                                                                                        |
| Grid             | ✅          |                                                                                                                                                                                                                        |
| Columns          | ✅          |                                                                                                                                                                                                                        |

### Styles

✅ Both Webflow and Webstudio support all CSS properties, allowing all styles defined in the style panel to transfer.

✅ Webflow preset styles, which Webflow adds by default to pages and Elements.

❌ Webflow variables and their values do not transfer as those values are not available within the Webflow copy.

❌ User-defined styles on **global tag selectors** as they are not available in the Webflow copy. For example, global styling on an H1 does not transfer, but a Token of h1 is created and available for applying the styles.

![Global tag styling in webflow](https://330243581-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTep4EGj6hSC54ozHujyK%2Fuploads%2Fgit-blob-3b846164470a7da4bd9eda677cb5fb56d308df9d%2Fglobal-tags.png?alt=media) ![h1 token in webstudio](https://330243581-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTep4EGj6hSC54ozHujyK%2Fuploads%2Fgit-blob-aaa05f1bc5bce4d2ca0c84c000c2a41ec9ce7fec%2Fh1-token.png?alt=media\&token=bf16ddae-81e7-4109-8938-9341906af6b3)

### Classes

✅ Classes and combo classes will transfer as [Tokens](/university/foundations/design-tokens) with their respective styles.

{% hint style="success" %}
Pasting a class/Token that already exists will not cause duplication or override it.
{% endhint %}

### Breakpoints

✅ Responsive styles and breakpoints will transfer.

### Interactions

❌ Interactions and animations do not transfer.

## Related

* [Copy & Paste](https://webstudio.is/copy-paste) – Learn more about copying from Webflow to Webstudio
* [CSS](/university/foundations/copy-paste/css) – Paste CSS to populate the Style Panel
* [Markdown](/university/foundations/copy-paste/markdown) – Paste Markdown to create components automatically
* [Referenced images](/university/foundations/copy-paste/images) – Understand image handling during paste
* [Shortcuts](/university/foundations/shortcuts) – Keyboard shortcuts for faster workflows
* [Commands and search](/university/foundations/commands-and-search) – Quick access to commands and settings
* [Anatomy of the Webstudio builder](/university/foundations/anatomy-of-the-webstudio-builder) – Overview of the Webstudio interface


# SVG

Paste SVG code into Webstudio as native SVG components.

You can paste SVG code directly onto the canvas. Webstudio converts compatible SVG markup into native SVG components, so use this when you want the vector markup to be editable in the Navigator.

## How to paste SVG

1. Copy SVG markup, starting with an `<svg>` element.
2. Paste it onto the canvas in Webstudio.
3. Select the generated SVG components to adjust styles and settings.

## Paste SVG code vs. upload an SVG file

Paste SVG code when you want access to each SVG node in the Navigator, such as paths, groups, shapes, and text.

If you only need to display the SVG as an image, drag the SVG file into the [Assets panel](/university/foundations/assets#uploading-assets) and use it with the [Image component](/university/core-components/image).

## Related

* [HTML Embed](/university/core-components/html-embed) – Embed custom HTML when you do not need native editable SVG components
* [Image](/university/core-components/image) – Display uploaded SVG files as image assets
* [Assets](/university/foundations/assets#supported-file-types) – See supported asset file types


# Referenced Images

Understand how referenced images are handled when pasting content.

When pasted content references image URLs, Webstudio uploads those images into [Assets](/university/foundations/assets) and rewrites the generated components/styles to use the uploaded assets.

This applies to paste flows that include image references, such as:

* [HTML with CSS](/university/foundations/copy-paste/html-with-css)
* [HTML with Tailwind](/university/foundations/copy-paste/html-with-tailwind)
* [Markdown](/university/foundations/copy-paste/markdown)
* [Webflow](/university/foundations/copy-paste/webflow)

This page is about images referenced by pasted text/markup. It is not a separate workflow for pasting raw image files from the system clipboard.

## Related

* [Assets](/university/foundations/assets) – Upload, search, organize, and delete assets
* [Image](/university/core-components/image) – Configure image source, alt text, loading, and optimization
* [Performance](/university/foundations/performance#image-optimization) – Understand image optimization


# Commands & search

The Commands & search Panel is a centralized place to navigate the builder and perform actions such as switching pages and adding components — all with the keyboard.

Commands & search speeds up the building process and introduces new shortcuts.

**Open Commands & search with ⌘ + K (Control + K for Windows).**

<picture><source srcset="/files/SnbAWJIAettzDCWuufCu" media="(prefers-color-scheme: dark)"><img src="https://330243581-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTep4EGj6hSC54ozHujyK%2Fuploads%2Fgit-blob-6ca71919ae5e7de54dd74b0304791b56e6c480ca%2Fcommands-search-light.png?alt=media" alt="Commands and search dialog listing Builder commands and their keyboard shortcuts"></picture>

{% embed url="<https://youtu.be/ofUP0Uc_ttY>" %}

## Actions

* Add components
* Search instances by name – find any instance in your project
* Switch breakpoints
* Switch pages
  * Open page settings
* Manage tokens – create, edit, and delete style tokens
* Manage data variables – view, edit, and organize your project's data variables
* Manage CSS variables – view all CSS custom properties defined in your project
* Execute commands
  * Unwrap – Move the children out of the current instance and remove the current instance.
  * Wrap In Box – Wrap the current instance in a Box.
  * Wrap In Link – Wrap the current instance in a [Link](/university/core-components/link).
  * Wrap In (any component) – Wrap the current instance in any component you choose.
  * Wrap In Element – Wrap the current instance in any HTML element.
  * Convert To – Convert the current instance to a different component type while preserving children and styles.
  * Replace With Element – Replace the current instance with a different HTML element.
  * Insert Tag – Insert specific HTML tags at the current position.
  * Paste HTML with Tailwind classes – Paste HTML from your clipboard and convert Tailwind utility classes into native Webstudio styles.
  * Delete Unused Variables – Remove data variables that are no longer referenced anywhere.
  * Delete Unused CSS variables – Remove CSS custom properties that are no longer used.
  * All other [keyboard shortcuts](/university/foundations/shortcuts)

## Secondary actions with Tab

Some items in the Commands Panel have secondary actions. Press **Tab** to access them. For example, when searching pages, you can press Tab and arrow to "Settings" to go directly to that page's settings.

## Find forgotten shortcuts

If you forget a shortcut like renaming an instance, search for the action (e.g., "edit instance label") and the Commands Panel will show the shortcut so you can learn it.

## Group counters

Each category in the Commands Panel shows a counter indicating how many items it contains, helping you quickly understand the scope of your project.

## Related

* [Shortcuts](/university/foundations/shortcuts) – Complete list of keyboard shortcuts
* [Anatomy of the Webstudio builder](/university/foundations/anatomy-of-the-webstudio-builder) – Overview of the Builder interface
* [Design tokens](/university/foundations/design-tokens) – Manage reusable style packages
* [CSS variables](/university/foundations/css-variables) – Manage CSS custom properties
* [Variables](/university/foundations/variables) – Manage data variables in your project


# Assets

Upload and manage images, fonts, and other files used in your project.

The Assets panel is located on the left side of the builder. It stores all static files used in your project — images, fonts, documents, and more. Upload files here and then reference them in instances and styles throughout your site.

<picture><source srcset="/files/8HXShDzjvvPraIzN8Zeu" media="(prefers-color-scheme: dark)"><img src="https://330243581-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTep4EGj6hSC54ozHujyK%2Fuploads%2Fgit-blob-7be4b45d79ffa250bf8f12d0a4e59eef0c2fc6bc%2Fassets-panel-light.png?alt=media" alt="Assets panel with search, filter and sort controls above folders and files"></picture>

## Supported file types

| Category        | Formats                                   |
| --------------- | ----------------------------------------- |
| **Images**      | JPEG, PNG, GIF, WebP, SVG, AVIF, ICO, BMP |
| **Fonts**       | WOFF, WOFF2, TTF, OTF                     |
| **Video**       | MP4, MOV, AVI, WebM                       |
| **Audio**       | MP3, WAV, OGG, M4A                        |
| **Documents**   | PDF, DOC, DOCX, XLS, XLSX, CSV, PPT, PPTX |
| **Code & text** | TXT, MD, MDX, JS, CSS, JSON, HTML, XML    |
| **Archives**    | ZIP, RAR                                  |

{% hint style="info" %}
JPEG, PNG, GIF, WebP, SVG, and AVIF images are automatically optimized and resized by Cloudflare. ICO and BMP images are served as-is without optimization.
{% endhint %}

## Uploading assets

Drag files directly into the Assets panel, drop them anywhere on the panel, or click the upload icon in the panel header. Multiple files can be uploaded at once. For images, you can also drag a URL directly from the browser to upload from an external source.

Open a folder before uploading to add the new assets directly to that folder.

### Add an image directly to the canvas

Drag one image asset from the Assets panel onto the canvas to insert an **Image** component with that asset already selected as its source. Drop it at the insertion indicator like a component from the Components panel. Dragging multiple selected assets does not insert multiple Image components.

### Create and edit text files

Open the add menu in the Assets panel and choose **Create text file**. Enter a supported filename, such as `notes.md`, `article.mdx`, or `data.json`. Webstudio creates the file in the current folder and opens it in the code editor. New JSON files start with an empty object so the Content Engine can index them immediately.

You can open uploaded `txt`, `csv`, `md`, `mdx`, `js`, `css`, `json`, `html`, `xml`, and `svg` assets in the same editor. Syntax highlighting follows the file type; unsupported text types use plain text. Markdown and MDX files also provide formatting controls and a preview.

MDX files use the same Markdown document features and additionally support Webstudio elements for content that Markdown cannot represent. Rename a `.md` file to `.mdx` to enable MDX parsing. Webstudio does not rewrite the file while renaming it; invalid MDX remains editable and reports source-located errors.

<figure><picture><source srcset="/files/CKW5pv9qd9bKnH3eMWqJ" media="(prefers-color-scheme: dark)"><img src="https://330243581-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTep4EGj6hSC54ozHujyK%2Fuploads%2Fgit-blob-6dfefd51afb31c3f6ff9e8ef156d261946920a0f%2Fmdx-editor-preview-light.png?alt=media" alt="MDX editor showing frontmatter and Markdown source beside the rendered preview"></picture><figcaption><p>Edit MDX source and review its rendered preview side by side.</p></figcaption></figure>

Edits save when the editor loses focus or when you press `Command + S` on macOS, `Ctrl + S` on Windows, or `Command/Ctrl + Enter`. Edit the complete filename in Asset settings to rename a text file or change its format. When you change the extension to `.json`, Webstudio validates the existing content and converts JSON-compatible syntax to strict JSON before saving the new file revision. JSON files can contain any JSON value, including arrays and scalars. The editor accepts unquoted object keys, single-quoted strings, and trailing commas. It reports unsupported syntax instead of saving the file. Converting an empty text file to `.json` initializes it with an empty object.

### Use assets as content

Markdown, MDX, and JSON files in Assets can be the source of truth for a site. The Content Engine reads their structured fields, queries the files, and resolves links between them. See [Content Engine](/university/foundations/content-engine) for the supported file structure and a complete article workflow. Connect an MDX file to a [Content Block](/university/core-components/content-block#store-content-in-an-mdx-file) when editors should update its body visually in Content mode.

## Organizing assets with folders

Create folders in the Assets panel to organize large asset libraries. Folders can contain both assets and other folders. Open a folder to view its contents, and use the breadcrumbs above the asset grid to move back through the folder hierarchy.

Assets remembers the last open folder for each project in your browser. Reopen the panel or reload the Builder to return to that folder. If the folder was deleted, the panel opens at the root instead.

You can:

* Drag assets and folders into another folder.
* Use **Move** to choose a destination without dragging.
* Cut, copy, paste, and duplicate assets or complete folder trees.
* Rename or delete folders.

Open **Folder settings** to change a folder's name or parent folder. Valid changes save automatically; there is no Save button. Blank names and names already used in the selected parent folder are not saved.

Duplicating a folder copies its nested folders and assets. Deleting a folder deletes everything inside it, so review the confirmation before continuing.

### Use a folder as a content collection

Turn on **Use as content collection** when creating a folder, or choose it from an existing folder's menu, when editors should create structured MDX entries without writing frontmatter by hand. Webstudio adds `collection.json`, which defines the entry fields, and an MDX entry template. The folder remains a regular Assets folder.

Use **Configure collection** to define the form fields, starting content, and the dynamic entry page. Editors can then choose **New entry**, update fields in **Entry settings**, edit the source through **Edit file**, or choose **Open on canvas** to edit the complete article visually. See [Content Engine](/university/foundations/content-engine#make-the-folder-a-content-collection) for collection setup, validation, and publishing behavior.

### Select and update multiple items

Select multiple assets and folders to move, copy, cut, duplicate, or delete them together:

* Hold `Command` on macOS or `Ctrl` on Windows and click to add or remove an item from the selection.
* Hold `Shift` and click to select a range.
* Drag across empty space in the asset grid to select items with a marquee.

The familiar `Command` or `Ctrl` shortcuts for copy, cut, paste, and duplicate work while the Assets panel is focused. Press `Backspace` to delete the selection. The panel scrolls automatically when you drag selected items near the top or bottom of a long list.

## Search

Type in the search field at the top of the Assets panel to filter assets and folders by name. Search can surface matching content inside nested folders.

## Filtering and sorting

Use the filter dropdown to show only a specific category: All, Images, Documents, Video, Audio, Code, Archives, or Fonts.

Sort assets by:

* **Alphabetical** — A→Z or Z→A
* **Date created** — newest or oldest first
* **File size** — largest or smallest first

Folders are included in the current search and sort order.

## Asset details

Hover any asset and click the gear icon to open its detail panel:

<picture><source srcset="/files/xWEnKZPdiW4tJJwz3N8s" media="(prefers-color-scheme: dark)"><img src="https://330243581-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTep4EGj6hSC54ozHujyK%2Fuploads%2Fgit-blob-d3d37771213a57787e0feab3880d221be84ce467%2Fassets-detail-light.png?alt=media" alt="Asset settings showing file size, type, dimensions, usage count, dates, name, folder, description, and ID"></picture>

* **File size** and **MIME type**
* **Dimensions** and **Aspect ratio** (images only)
* **Uses** — how many places in the project reference this asset
* **Name** — editable; used as the filename in URLs
* **Description** — used as the default `alt` text for images
* **ID** — unique identifier, can be copied to clipboard

## Deleting assets

Delete and download buttons are available inside the asset detail panel (gear icon on hover):

* **Unused assets** can be deleted immediately.
* **Assets in use** show a "Review & delete" button that lists every usage with clickable links to each location, so you can review the impact before confirming.
* **Delete unused assets** — click the brush icon in the Assets panel header to review all unreferenced assets. Every asset is selected by default. Uncheck assets you want to keep, or use **Select all** to change the complete selection. Confirming deletes only the selected assets.

## Downloading assets

You can download any original asset file to your computer. Downloading is available on the Pro plan.

## Using assets

Once uploaded, assets are available in:

* **Image component** — select an asset as the image source
* **Background image** — pick an asset in the Style Panel under Backgrounds
* **Custom fonts** — uploaded font files are automatically available in the Typography section of the Style Panel

## Organizing assets with AI agents

Webstudio MCP exposes the same folder hierarchy to connected agents. An agent can list, create, rename, move, recursively duplicate, and recursively delete folders. It can also upload assets into a folder or move existing assets between folders. This keeps automated asset work visible and editable in the Builder.

## Related

* [Content Engine](/university/foundations/content-engine) – Build sites from Markdown, MDX, and JSON files in Assets
* [Content Block](/university/core-components/content-block) – Edit an MDX document visually in Content mode
* [Anatomy of the builder](/university/foundations/anatomy-of-the-webstudio-builder) – Overview of all builder panels
* [Image](/university/core-components/image) – Display images from assets or external URLs
* [Commands & search](/university/foundations/commands-and-search) – Quickly find and delete unused assets
* [How to use custom fonts](/university/how-tos/how-to-use-custom-fonts) – Upload and apply font files


# Content Engine

Create a file-based blog with Markdown or MDX, Assets queries, and dynamic pages.

Webstudio's Content Engine turns Markdown, MDX, and JSON files in Assets into content you can query and bind in the visual editor. The files remain the source of truth, including their filenames, folders, metadata, and relative links. You can move the same files between projects or use them outside Webstudio without exporting them from a database first.

MDX is a widely used file format that combines Markdown with JSX. In Webstudio, it lets designers pre-style every article element while editors write directly on the canvas with a Notion-style interface. Editors can also work in the MDX source, and AI agents can work through Webstudio MCP.

It brings Content mode to file-based sites without requiring an external CMS or a runtime content database on the published site. Use it to:

* Query file fields and render hundreds of pages from one visual design.
* Edit complete MDX articles on the canvas with changes saved back to the file.
* Give editors pre-styled Markdown elements and custom interactive components.
* Create and edit structured entries through forms generated from a collection schema.
* Build or change the same setup with Webstudio MCP while keeping every setting available in the visual interface.

This guide creates a blog overview at `/blog` and one dynamic article page at `/blog/:slug`.

To see the finished setup first, start with the [Markdown Blog marketplace template](https://webstudio.is/marketplace/templates/markdown-blog).

Watch the complete workflow, from the first MDX file to visual editing and structured collection entries:

{% embed url="<https://youtu.be/Sn-fhVajpgU>" %}

<figure><img src="https://330243581-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTep4EGj6hSC54ozHujyK%2Fuploads%2Fgit-blob-55b6dc49eabdd98eaa5568d80437bca9d41d42cc%2Fcontent-engine-assets-structure.png?alt=media" alt="Assets panel showing Markdown articles and their assets folder"><figcaption><p>Markdown articles stored alongside their assets</p></figcaption></figure>

## Choose Markdown or MDX

Choose the file format based on how editors will work with the article body:

| Format                      | Publishing                                                                                                                                   | Editing in Content mode                                                                                                   |
| --------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------- |
| `.mdx` with a Content Block | Publishing downloads the article files and compiles the MDX for every article the page can render. More articles can make publishing slower. | Editors can change the article visually on the canvas with a Notion-style experience, or edit the MDX source file.        |
| `.md` with Markdown Embed   | No per-article MDX compilation is needed. A hosted page reads the selected Markdown body when it is requested.                               | Editors can edit the `.md` file in the Markdown source editor in Assets, but cannot edit its body visually on the canvas. |

Hosted builds receive a content database for their Assets queries. Markdown body-reference queries use stored frontmatter for that database and fetch the selected body when a visitor opens the page. Static exports download article sources and build the database in the publish runner; this transfer and any MDX compilation still take time. Hosted pages using MDX Content Blocks also download article sources and compile the required MDX in the publish runner. If required MDX content is unavailable or compilation reports an error, the build fails instead of publishing an incomplete page. Use [Content Block](/university/core-components/content-block) when visual article editing matters, or [Markdown Embed](/university/core-components/markdown-embed) when source editing is enough.

## Decide if the Content Engine fits

Use the Content Engine for bounded, file-based content that should live with the site and remain portable as Markdown, MDX, or JSON.

Good fits include:

* Blogs with hundreds of text-and-image articles, within the limits below.
* Portfolios, team directories, resource libraries, and case studies.
* Small product catalogues with infrequently changed display data. Keep orders, inventory, and payments in an ecommerce system.

Use an external CMS, commerce backend, or media service when you need:

* Several thousand entries or complex search and filtering.
* Large image or video galleries, media processing, or streaming.
* Live inventory, customer-specific prices, editorial workflows, scheduled publishing, or content shared across many applications.

### Limits that affect this choice

Queries can consider and return at most 1,000 candidate documents. When a query uses document `$ref` values, resolving those references can load up to 2,000 documents across the candidate documents and their dependencies. All reachable Assets resources share a 500 KiB published content database. **Markdown body reference** keeps article bodies out of the database.

Images and videos remain separate Assets, but the Content Engine does not provide media processing or streaming. Review the complete [query and content limits](/university/foundations/content-engine/content-engine-reference#query-limits) before using it for a content-heavy project.

## Build it with MCP

An AI agent can complete this entire workflow through [Webstudio MCP](/university/mcp). It can create the folders and MDX files, upload images, build both pages, configure the Assets resources, connect a Content Block, add the content collection, set the page metadata, and check the rendered result with vision. Everything remains editable in the visual editor.

Give the agent an editable project share link when it asks for one, then make a request such as:

> Install Webstudio CLI and use MCP to build a blog with the Content Engine. Store complete articles in MDX files, create `/blog` and `/blog/:slug`, make every article field editable in Content mode, let editors create entries with a collection form, exclude drafts, and check both pages on desktop and mobile.

The remaining steps explain the same workflow when you want to build or inspect it manually.

## 1. Organize the files

The Content Engine does not require a particular folder structure. This guide uses the following organization in the Assets panel:

```
blog/
  posts/
    assets/
```

Keeping all articles in one folder makes them easy to query. Put images and other supporting files in a nested folder.

Open the settings for the `posts` folder and copy its ID. Both Assets resources in this guide use that ID to query only files in this folder.

### Make the folder a content collection

This step is optional. The Assets queries and dynamic page work without a collection. Add one when editors should create and update entries through forms instead of editing MDX frontmatter by hand.

Turn on **Use as content collection** when you create the `posts` folder if editors should be able to add articles without writing MDX frontmatter. For an existing folder, choose **Use as content collection** from its context menu, actions menu, or **Folder settings**, then confirm setup. This creates two files in the same folder without changing its ID or existing content:

* `collection.json` defines the entry fields and their rules with JSON Schema.
* `template.mdx` supplies the starting frontmatter and body for each entry.

Existing selected entries must be MDX files. Setup leaves their content unchanged, even when fields are missing or not yet configured. The collection shows field errors for repair after setup. Other files stay visible and are ignored by collection validation. Setup stops if a selected file has an incompatible type or conflicts with the generated configuration or template. Choose **Configure collection** after setup to define the fields used by your existing entries.

### Select collection entries

The generated `collection.json` includes filename patterns in `x-webstudio.entries`:

```json
{
  "x-webstudio": {
    "template": "template.mdx",
    "entries": ["*.mdx"]
  }
}
```

These use [URLPattern syntax](https://developer.mozilla.org/en-US/docs/Web/API/URL_Pattern_API), not filesystem globs. Patterns match direct filenames in the current folder, case-insensitively; subfolders are independent. For example, use `["post-*.mdx", "!post-private-*.mdx"]` to select public post files. A leading `!` excludes a pattern; exclusions take priority over inclusions. At least one inclusion is required. Slashes are not allowed. The configuration and configured template are always excluded from entries, regardless of patterns.

Omitting `entries` preserves the default `["*.mdx"]`. The setting accepts up to 64 patterns of 256 characters each. New entry filenames must match the patterns. Only MDX entries are currently supported; a pattern selecting another format reports an error rather than treating that file as editable MDX. Files not selected remain visible and can still appear in ordinary Assets query results.

### Configure fields and the template

The folder remains a normal Assets folder. The direct `collection.json` file is what makes Webstudio treat it as a collection and show the collection badge. The configuration and referenced template files also show this badge. You can open them or download them when permitted, but cannot cut, copy, duplicate, move, or delete them individually. Manage them through **Collection settings**. Their asset **Settings** remain available to collection designers: Name is read-only and Folder is disabled, while Description remains editable.

Open **Collection settings → Fields** and select a field from the list. Edit **Label** and **Field key** together, add a **Description** that tells editors what to enter, then choose the field's **Type**. The description appears as help in entry forms without repeating the field label. Set required fields and length or number limits under **Validation**. Choose **Add field** to add another entry field. The configurator writes the schema; designers do not need to edit JSON.

Use the **Entry template** tab to rename the entry template and edit its default frontmatter and starter content with the built-in Markdown editor. Creating an entry keeps you on the current page.

Use the **Entry page** tab to choose the dynamic page that displays collection entries. The page must have one URL parameter, such as `/blog/:slug`. Webstudio fills the page parameter with the entry filename when you choose **Open on canvas** from an entry's menu or **Entry settings**. The action stays visible but disabled until the collection has a compatible entry page; its help points back to this setting. If the page does not exist yet, finish step 5 and then return to this setting. Creating an entry does not navigate automatically.

The collection format uses a supported subset of JSON Schema draft 2020-12. The configurator exposes string, number, integer, boolean, and slug fields, along with required fields and length or value limits. Set starting values in the entry template frontmatter. Collection fields are flat; arrays and nested objects are not supported. Slug fields reference Webstudio's bundled schema:

```json
{
  "$ref": "https://webstudio.is/schemas/slug",
  "title": "URL slug",
  "description": "The URL-safe name used in this entry's web address.",
  "maxLength": 120,
  "x-webstudio": { "control": "slug" }
}
```

Webstudio resolves this reference from its bundled rules, without a network request. Length limits alongside the reference still apply. The configurator writes the reference automatically; designers do not need to write a regex. Other references, composition keywords, enums, formats, and custom regular-expression patterns are not supported. Webstudio reports unsupported rules with their location instead of silently ignoring them.

Editors choose **New entry**, complete the generated form, and select **Create entry**. Webstudio creates a lowercase, dash-separated slug from the configured source field, which is **Title** by default. Slugs preserve letters and numbers from any language, including accents: `Привет мир` becomes `привет-мир`, and `你好世界` stays `你好世界`. Spaces and punctuation become separators. If the title contains only emoji or symbols, enter a slug manually. Existing filenames are never renamed.

Webstudio recognizes its previously generated slug patterns and uses the bundled Unicode rule for those collections too. Saving collection settings replaces the old pattern with the reference. Existing entry content and filenames remain unchanged. Future rule updates do not require editing each collection's regex.

Editors can change the slug before creating the entry. New entries must satisfy the schema. Existing entries report field errors without blocking incremental edits or publication. The slug becomes the MDX filename and cannot be changed after creation.

Slug fields are optional. Change the Slug field to another type if the collection does not need one. New entries then receive unique generated MDX filenames, without adding a filename field to their frontmatter. Existing filenames stay unchanged. For a manual slug, set **Generate from** to **None (manual entry)**.

For optional fields, click the field label and choose **Reset value**, or Alt-click the label, to omit the value, including a value supplied by the template. An empty text value is different from an omitted value. Optional boolean fields offer **Yes**, **No**, and **Not set**. Validation errors appear beside their fields.

Collection settings save automatically when valid. Webstudio checks the entry template against the new rules. Existing entries remain editable if a rule changes. The open collection folder checks entry frontmatter in the background and shows a warning when an entry needs attention. Choose **Review first entry**, select an entry's error indicator, or choose **Entry settings** from its menu to edit its fields using the same controls as **New entry**. Edits save automatically, including partial repairs. Unknown frontmatter properties and the MDX body are preserved. Slugs remain fixed to the filename; an existing mismatch has a repair action.

Use **Edit file** to edit the body or inspect unconfigured fields. Use **Open on canvas** to edit the configured entry page visually. Invalid field values are underlined in the source editor; hover them for details. Content-mode text and number controls bound to these fields also show their collection errors. Fixing the values clears the errors. A field error does not hide the entry, stop the collection rendering, or block publication of unrelated changes.

A failed read is reported separately from field errors and can be retried with **Check again**.

The configurator rejects Integer limits that leave no possible whole number. Slug limits must allow at least one character, even when minimum length is unset. Without a Slug field, use a single `*.mdx` or `entry-*.mdx` entry pattern when saving collection settings. These match automatically generated filenames. Custom filename patterns require a Slug field. Existing configurations remain readable so their content can still be repaired and published.

A collection folder accepts entries, supporting assets, and subfolders. Use **New entry** for filenames selected by the entry patterns. Files excluded by the patterns behave like ordinary assets: upload, create, paste, move, rename, and duplicate them normally. The collection configuration and template remain protected and do not appear in Content Engine query results. Keep `collection.json` valid and its referenced template available. Broken collection configuration can still block collection queries and publishing when Webstudio cannot safely identify the reserved files. An invalid template still blocks entry creation and publication. Malformed YAML and unreadable files are not ordinary field-validation errors; those existing parsing and access checks still apply.

**Delete unused assets** does not list the files directly inside a collection folder. Collection entries are loaded dynamically and may not have a direct page or component reference.

Choose **Convert to regular folder** in **Collection settings**, the folder's context or actions menu, or the empty-space context menu inside the folder. Confirm the conversion. This deletes only `collection.json`. The folder becomes a normal folder again, while its entries and template remain. The former template is then a regular MDX file and can appear in Assets query results.

## 2. Create an article

If `posts` is a content collection, choose **New entry** and fill in its form. Otherwise, create the MDX file and frontmatter manually:

1. Open `blog/posts` in the Assets panel.
2. Open the add menu and choose **Create text file**.
3. Name the file `hello-world.mdx`.
4. Add the article metadata between the two `---` lines, followed by the article body:

```markdown
---
title: Hello world
slug: hello-world
publishedAt: 2026-08-10
excerpt: A short introduction to the article.
draft: false
featureImage:
  $ref: ./assets/hello-world.png
author: Ada Lovelace
---

# Hello world

Write the article here.
```

<figure><img src="https://330243581-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTep4EGj6hSC54ozHujyK%2Fuploads%2Fgit-blob-181ab8e7f8915a3301eb130c9861547cdb24d7dc%2Fcontent-engine-markdown-editor.png?alt=media" alt="Markdown editor showing article frontmatter and body"><figcaption><p>An article's metadata and body in the Markdown editor</p></figcaption></figure>

### Frontmatter

The opening YAML block is called **frontmatter**. It must be the first block in the Markdown file and must start and end with `---` on separate lines.

Frontmatter accepts strings, numbers, booleans, `null`, arrays, and nested objects. The Content Engine exposes these fields under `properties`, such as `properties.title` and `properties.slug`.

You define the fields yourself. Keep their names and value types consistent between articles so one query and one page design work for every article.

{% hint style="warning" %}
`draft` is a field you defined, not the visual editor's automatic [page draft](/university/foundations/page-settings#draft-pages) setting. Add a query filter that excludes `draft: true` anywhere unpublished articles must not appear.
{% endhint %}

The Markdown below the closing `---` is the body. It is available separately at `content.text` when the Assets resource uses **Markdown body reference**.

### Relative asset paths

Upload `hello-world.png` to `blog/posts/assets`. The article's `$ref` resolves relative to the Markdown file. It declares that `featureImage` is structured asset data without deciding which metadata a query must return.

Relative paths also work in nested objects, arrays, and JSON files. Query strings and fragments are preserved, so a value such as `./assets/hello-world.png?width=1200#cover` remains usable.

Select only the referenced fields the page uses, such as `properties.featureImage.src` and `properties.featureImage.description`. The value becomes structured data:

```js
post.properties.featureImage.src;
post.properties.featureImage.description;
```

Bind the Image source to `post.properties.featureImage.src`. Bind its alternative text to `post.properties.featureImage.description ?? post.properties.title`. This uses the Asset Manager description when present and falls back to the article title. Existing content that stores the asset path as a plain string continues returning a URL string.

Imported content can also use published asset paths such as `./assets/hero.png` or `/assets/hero.png`. Webstudio matches the final filename across project Asset folders, so the content file and referenced asset do not need to share the same folder hierarchy. The filename must identify exactly one project asset.

Webstudio resolves a path only when it uniquely matches a project asset. External URLs, other root-relative URLs, missing paths, and ambiguous paths remain unchanged. Keep the clean path in the source instead of hardcoding an asset ID or generated filename. This is what makes the content portable.

### JSON files

The Content Engine can query JSON files as well as Markdown and MDX. JSON files can contain objects, arrays, or scalar values. A root object exposes its fields for structured queries:

```json
{
  "name": "Ada Lovelace",
  "role": "Author",
  "avatar": "./assets/ada.png"
}
```

The object's fields are exposed under `properties`, such as `properties.name` and `properties.avatar`. Root arrays and scalars remain valid JSON content but do not expose top-level `properties` fields. Use JSON when the file contains structured data without a Markdown body. Name the file with a `.json` extension in the **Create text file** dialog. A new file starts with an empty object, which you can replace with any JSON value. The editor accepts JSON-compatible syntax and formats it as strict JSON when saving. Unsupported or incomplete syntax is reported without saving the file. You can also change an existing text file's extension to `.json` by editing its complete filename in Asset settings; Webstudio validates and formats the current content before converting it.

## 3. Query the articles for the overview

Create a static page with the path `/blog`, then select its Body and add an Assets resource in **Settings > Variables**:

1. In the **New variable** dialog, choose **Assets resource**.
2. Name it `posts`.
3. Under **Output**, choose **Selected fields**, turn off **File metadata**, and include only the fields the overview renders, such as `properties.title`, `properties.slug`, `properties.publishedAt`, `properties.excerpt`, and `properties.featureImage.src` and `properties.featureImage.description`.
4. Under **Result**, choose **Many**.
5. Under **Content**, choose **Metadata only**. The overview does not render complete article bodies.
6. Add these filters:
   * `extension` **equals** `"mdx"`
   * `folder id` **equals** the quoted `posts` folder ID
   * `properties.draft` **does not equal** `true`
7. Sort `properties.publishedAt` in descending order. Add `id` in ascending order as a second sort so articles with the same publication date keep a stable order.

<figure><img src="https://330243581-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTep4EGj6hSC54ozHujyK%2Fuploads%2Fgit-blob-fc0bd75476ed2ae7d9ac46e2ee386cfc9206838c%2Fcontent-engine-overview-query.png?alt=media" alt="Assets overview query filtering Markdown files and drafts, then sorting by publication date and ID"><figcaption><p>An overview query for the Webstudio Updates project</p></figcaption></figure>

Choosing only the fields the page renders keeps the published content data small. Leaving article bodies out of the overview also avoids loading every article just to display a list.

## 4. Build the overview

1. Add a [Collection](/university/core-components/collection) to the page.
2. Bind the Collection data to `posts.data`.
3. Rename the Collection Item to `Post`.
4. Design one article card inside the Collection.
5. Bind the card's text and image to fields on `Post.value`, such as `Post.value.properties.title` and `Post.value.properties.featureImage.src`.
6. Bind the image alternative text to `Post.value.properties.featureImage.description ?? Post.value.properties.title`.
7. Bind the card link to `"/blog/" + Post.value.properties.slug`.

The Assets resource returns many results as an object keyed by asset ID. The current article inside the Collection is available through the Collection Item's `value`.

## 5. Create the dynamic article page

Create one page with the path `/blog/:slug`. This page is the template for all articles. In the visual editor's address bar, enter `hello-world` as the preview value for `:slug`.

On the article page, select Body and add another Assets resource in **Settings > Variables**:

1. In the **New variable** dialog, choose **Assets resource**.
2. Name it `post`.
3. Under **Output**, choose **Selected fields**, turn off **File metadata**, and include the fields this page renders, such as `properties.title`, `properties.publishedAt`, `properties.excerpt`, `properties.featureImage.src`, `properties.featureImage.description`, and `properties.author`.
4. Under **Result**, choose **Exactly one**.
5. Under **Content**, choose **Metadata only**. The Content Block loads the matching MDX file directly.
6. Add these filters:
   * `extension` **equals** `"mdx"`
   * `folder id` **equals** the quoted `posts` folder ID
   * `properties.slug` **equals** `system.params.slug`
   * `properties.draft` **does not equal** `true`

<figure><img src="https://330243581-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTep4EGj6hSC54ozHujyK%2Fuploads%2Fgit-blob-6d9bc65c16af9ad50f7eb77db4744e350d2289b1%2Fcontent-engine-article-query.png?alt=media" alt="Assets resource filtering one Markdown article by folder and the dynamic page slug"><figcaption><p>The article Resource uses the dynamic slug from the page URL</p></figcaption></figure>

**Exactly one** returns the matching article directly at `post.data`. It also reports an error if two articles use the same slug. You do not need a Collection on the article page.

**Metadata only** keeps article bodies out of the published content database. The resource finds the matching article and returns its Asset ID. The Content Block then loads that MDX file directly.

If `posts` is a content collection, return to **Collection settings → Entry page** and select this dynamic page. This enables **Open on canvas** for its entries.

## 6. Bind the article

Add a [Content Block](/university/core-components/content-block#store-content-in-an-mdx-file) and bind its **Source** to `post.data.id`. Every Assets query result includes its Asset ID, even when file metadata is disabled. The Content Block loads the matching article and adds an **MDX content** region for its body.

Build the complete article inside the Content Block:

* Bind the heading directly to `document.frontmatter.title`.
* Bind the author directly to `document.frontmatter.author`.
* Bind the Image source directly to `document.frontmatter.featureImage.src` and make the binding writable.
* Bind the Image alternative text to `document.frontmatter.featureImage.description`.
* Place the **MDX content** region where the article body should render.
* Style the standard Markdown elements in **Templates**, then add any custom interactive elements editors should be able to insert.

Keep each editable value directly bound to one frontmatter field. Computed expressions remain read-only. Put fixed wording in separate text elements and use a Date Time component for dates so Content mode shows the appropriate controls.

Editors can now change the title, author, images, dates, body, links, and custom interactive elements directly on the canvas. These changes save back to the MDX file. Verify every article-owned value in Content mode before handing the site to an editor.

In Page Settings, bind the fields needed for search and sharing:

* **Title**: `post.data.properties.title`
* **Description**: `post.data.properties.excerpt`
* **Social image**: `post.data.properties.featureImage.src`
* **Status code**: `post.data ? 200 : 404`

The status expression returns a real 404 when no article matches the URL.

### Render a Markdown body without visual editing

Use a `.md` file and [Markdown Embed](/university/core-components/markdown-embed) when editors can work in the Markdown source editor instead of editing the body on the canvas. Content mode editors can still open and edit the `.md` file in Assets. Set the article resource's `extension` filter to `md` and **Content** to **Markdown body reference**, then bind the Markdown Embed code to `post.data.content.text`. Style its nested document elements once for every article. See [Choose Markdown or MDX](#choose-markdown-or-mdx) for the publishing tradeoff.

## 7. Publish the article

Start new articles with `draft: true`. The overview and article queries above will exclude them. Change `draft` to `false` when the article is ready:

```yaml
draft: false
```

The overview query will now include it. If you build a [custom sitemap](/university/core-components/xml-node) for the dynamic article URLs, give its Assets resource the same `properties.draft does not equal true` filter. The metadata field does not automatically remove an article from a custom sitemap.

To publish another collection article, choose **New entry**. For the manual workflow, duplicate the MDX file and change its title, slug, publication date, image, and body. The existing overview and dynamic page will render it without another page design.

## Connect content with document references

The Content Engine calls links between Markdown, MDX, and JSON files **document references**. A document reference replaces an exact `$ref` object with data from another content file. This is Webstudio's syntax. It uses URI references for file paths and [JSON Pointer](https://datatracker.ietf.org/doc/html/rfc6901) for values inside JSON files, but it does not implement JSON Schema resolution.

All supported source formats can reference the other content formats:

| Source          | Where references can appear | JSON target | Markdown or MDX target |
| --------------- | --------------------------- | ----------- | ---------------------- |
| Markdown or MDX | YAML frontmatter            | Yes         | Yes                    |
| JSON            | Anywhere in the document    | Yes         | Yes                    |

References do not work inside a Markdown or MDX body. A `$ref` object can be nested in an object or array, including inside a file reached through another reference.

### Reference syntax

A reference is an object with one field:

```json
{ "$ref": "<relative-path>[#<fragment>]" }
```

The object must contain only `$ref`, and its value must be a string. An object that has `$ref` plus another field remains ordinary content. Resolve the path relative to the file containing the reference, not the project root.

The optional fragment selects which value to insert:

| Reference                           | Value inserted at `$ref`                            |
| ----------------------------------- | --------------------------------------------------- |
| `../authors/ada.json`               | The complete JSON value                             |
| `../authors/ada.json#/profile/name` | The value at JSON Pointer `/profile/name`           |
| `../authors/ada.md`                 | The complete Markdown source, including frontmatter |
| `../authors/ada.md#frontmatter`     | The Markdown frontmatter as an object               |
| `../authors/ada.md#body`            | The Markdown body without frontmatter               |

JSON Pointer fragments apply only to JSON files. Use `~1` for `/` and `~0` for `~` inside a property name. For example, `#/social~1links/0` selects the first item in a property named `social/links`. URI-encode characters that belong to a filename but have a special meaning in a URL. A file named `draft#1.json`, for example, becomes `draft%231.json` in a reference.

### Reference Markdown from Markdown

For example, keep author details in `blog/authors/ada.md` and insert its frontmatter into an article in `blog/posts`:

```yaml
author:
  $ref: ../authors/ada.md#frontmatter
```

The queried article exposes the result under `properties.author`. Bind the author's name with `post.data.properties.author.name`.

### Reference JSON from JSON

JSON files use the same syntax. This post references one value from an author file:

```json
{
  "title": "Hello world",
  "author": { "$ref": "../authors/ada.json#/profile" }
}
```

Given this `ada.json` file:

```json
{
  "profile": {
    "name": "Ada Lovelace",
    "role": "Author"
  }
}
```

the resolved `properties.author` value is:

```json
{
  "name": "Ada Lovelace",
  "role": "Author"
}
```

The same rules cover JSON to Markdown or MDX and Markdown or MDX frontmatter to JSON. A referenced file can contain its own references, so shared records can be composed across several files.

The Content Engine loads referenced data when a query filters, sorts, or returns a field that depends on it. The target must be another Markdown, MDX, or JSON file in the project's compiled Assets. Missing files, invalid fragments, and reference cycles fail instead of returning partial data.

## Related

* [Content Engine reference](/university/foundations/content-engine/content-engine-reference) – Check query fields, modes, diagnostics, references, and limits
* [Assets](/university/foundations/assets) – Create, edit, organize, and reference project files
* [Content Block](/university/core-components/content-block) – Design and visually edit MDX articles
* [Variables](/university/foundations/variables) – Define resources and understand their scope
* [Collection](/university/core-components/collection) – Render article lists
* [Markdown Embed](/university/core-components/markdown-embed) – Render and style an article body


# Content Engine reference

Query Markdown, MDX, and JSON Assets with the Content Engine.

Assets resources query Markdown, MDX, and JSON files stored in the Assets panel. The Builder and Webstudio MCP use the same structured query contract.

## MCP workflow

Use these tools in order when creating or changing an Assets resource:

1. Call `get-asset-field-catalog` to inspect standard fields and the fields currently observed in Markdown or MDX frontmatter and JSON files.
2. Call `validate-asset-query` to check the query structure, field paths, operators, and bounded operation counts.
3. Call `preview-asset-query` with concrete values and inspect its results and diagnostics.
4. Save the query with `create-assets-resource` or `update-assets-resource`.
5. Inspect saved queries with `list-assets-resources` or `get-assets-resource`. Use `delete-resource` to remove an obsolete resource.

Omit `query` when creating a resource to use the default many-result query for asset URLs and image dimensions. Set `values.query` to `null` when updating a resource to restore that default.

## Fields

Every asset has the standard fields below. Markdown or MDX frontmatter and JSON root fields appear under `properties`, for example `properties.slug` or `properties.author.name`. The field catalog reports their observed types, occurrence counts, optionality, and mixed-type state. A JSON content file must contain an object at its root.

| Field         | Observed type |
| ------------- | ------------- |
| `id`          | `string`      |
| `url`         | `string`      |
| `width`       | `number`      |
| `height`      | `number`      |
| `name`        | `string`      |
| `description` | `string`      |
| `path`        | `string`      |
| `key`         | `string`      |
| `folderId`    | `string`      |
| `extension`   | `string`      |
| `mimeType`    | `string`      |
| `size`        | `number`      |
| `createdAt`   | `string`      |
| `revision`    | `string`      |
| `excerpt`     | `string`      |

## Filters

Put conditions under `where.all` when every condition must match, or under `where.any` when at least one condition must match. Groups can be nested. A field path is an array such as `["properties", "slug"]`.

| Operator     | Builder label         | Compatible observed types                                |
| ------------ | --------------------- | -------------------------------------------------------- |
| `eq`         | equals                | `null`, `boolean`, `number`, `string`, `object`, `array` |
| `ne`         | does not equal        | `null`, `boolean`, `number`, `string`, `object`, `array` |
| `contains`   | contains              | `string`, `array`                                        |
| `startsWith` | starts with           | `string`                                                 |
| `endsWith`   | ends with             | `string`                                                 |
| `gt`         | greater than          | `number`, `string`                                       |
| `gte`        | greater than or equal | `number`, `string`                                       |
| `lt`         | less than             | `number`, `string`                                       |
| `lte`        | less than or equal    | `number`, `string`                                       |
| `in`         | is one of             | `null`, `boolean`, `number`, `string`, `object`, `array` |
| `exists`     | exists                | `null`, `boolean`, `number`, `string`, `object`, `array` |
| `isEmpty`    | is empty              | `string`, `object`, `array`                              |

The field catalog determines which operators fit a schemaless `properties` field. `exists` and `isEmpty` take a boolean. `in` takes an array. Other operators take one JSON value. When filtering `path`, enter the visible folder and file names, such as `Tutorial posts/article.mdx`; Webstudio handles canonical URL encoding. Existing encoded path filters remain supported.

## Saved values and preview values

Queries saved with `create-assets-resource` or `update-assets-resource` accept expressions for filter values, limits, and offsets. Wrap fixed values as literals. Pass a JavaScript expression string only when the value must be resolved at runtime:

```json
{
  "where": {
    "all": [
      { "field": ["extension"], "operator": "eq", "value": { "type": "literal", "value": "md" } },
      { "field": ["properties", "slug"], "operator": "eq", "value": "system.params.slug" }
    ]
  },
  "limit": { "type": "literal", "value": 1 },
  "offset": { "type": "literal", "value": 0 }
}
```

`validate-asset-query` and `preview-asset-query` execute a concrete query. Pass resolved JSON values such as `"hello-world"` and `1`, not expression wrappers or expression code.

## Sorting and pagination

Each sort has a field path and an `asc` or `desc` direction. Add `id` as the final sort when equal values must keep a stable order. `limit` defaults to 20 and `offset` defaults to 0. Static filters, limits, and offsets should use literal values. Use expressions only for runtime values such as `system.params.slug`.

## Result modes

| Value   | Behavior                                                                          |
| ------- | --------------------------------------------------------------------------------- |
| `many`  | Returns every matching item up to the limit. Use it for listings.                 |
| `one`   | Returns one item or `null`. It fails when more than one document matches.         |
| `first` | Returns the first sorted item or `null`. The query must include an explicit sort. |
| `last`  | Returns the last sorted item or `null`. The query must include an explicit sort.  |

Every returned item includes `id`. In `preview-asset-query`, a many result has `data.items`, `data.totalCount`, and `data.hasMore`; a single result has `data.item` and `data.totalCount`. A saved Assets resource exposes a many result as an ID-keyed map at `<dataSource>.data`, with `totalCount` and `hasMore` at `<dataSource>.meta`. It exposes a single result as the item or `null` directly at `<dataSource>.data`, with `totalCount` at `<dataSource>.meta`.

## Output modes

| Value    | Behavior                                                                                                         |
| -------- | ---------------------------------------------------------------------------------------------------------------- |
| `all`    | Returns every indexed property and the excerpt. Use selected fields when the page needs only part of a document. |
| `base`   | Returns no `properties` or excerpt. Set `includeMetadata` to include the standard file metadata.                 |
| `fields` | Returns the paths in `fields`. Set `includeMetadata` separately when the page also needs standard file metadata. |

Choose `fields` and disable `includeMetadata` when the page needs only selected values. Fields used only for static filtering or sorting do not need to be returned. When enabled, `includeMetadata` adds `name`, `description`, `path`, `key`, `folderId`, `extension`, `mimeType`, `size`, `createdAt`, `revision`. Every result includes `id`.

## Asset reference metadata

A plain local asset path in Markdown or MDX frontmatter, or in JSON properties, keeps the backward-compatible URL string result. Wrap the path in an exact `$ref` object only when consumers need structured Asset Manager metadata:

```yaml
featureImage:
  $ref: ./assets/hero.png
```

The reference declares only the relationship. The query decides which metadata enters its result and published database. A complete `properties.featureImage` selection returns the Asset Manager asset metadata together with its resolved `src`. This includes fields such as `id`, `name`, `description`, `mimeType`, `width`, and `height`; new asset metadata becomes available to references without changing the document. Select only the nested fields the page uses:

```json
{
  "output": {
    "mode": "fields",
    "includeMetadata": false,
    "fields": [
      ["properties", "title"],
      ["properties", "featureImage", "src"],
      ["properties", "featureImage", "description"]
    ]
  }
}
```

The selected value becomes an object. Bind an Image source to `post.properties.featureImage.src` and its alternative text to `post.properties.featureImage.description ?? post.properties.title`. A missing description therefore falls back to the article title without duplicating text in frontmatter.

External URLs remain ordinary strings. Existing local path strings also keep their URL string shape, so adopting structured references does not change existing queries or bindings.

## Content modes

| Value               | Behavior                                                                                                                                                                                                                                          |
| ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `none`              | Returns no file content. Use this for listings and any query that only needs fields or metadata.                                                                                                                                                  |
| `full`              | Embeds the complete UTF-8 file content in the content database. `maxBytes` defaults to 1 MiB and cannot be set higher. The query fails if a selected file is larger.                                                                              |
| `range`             | Embeds a byte range selected by `offset` and `length` in the content database. `length` cannot exceed 256 KiB.                                                                                                                                    |
| `markdown-body-ref` | Stores a reference to a Markdown or MDX body. Webstudio filters and paginates first, then reads only the selected bodies from Assets. `maxBytes` defaults to 1 MiB and cannot be set higher. The query fails if a selected source file is larger. |

Returned content has `encoding` and `text`. A range also reports its `offset`, returned `length`, and total file size. Use `markdown-body-ref` for Markdown or MDX article pages. It keeps article bodies out of the published content database and resolves relative links when the selected body is loaded.

## Preview diagnostics

`preview-asset-query` returns renderable results in `data` and non-bindable statistics in `__diagnostics__`. The diagnostic `scope` is always `query-preview`. Read the two capacity scopes separately:

* `query` measures the temporary database for the query being previewed.
* `database` measures the merged database for all reachable Assets resources in the project.

Only `database.usedBytes` counts toward `database.maxBytes`. Do not add the query and database sizes together.

| Diagnostic              | Meaning                                                                                                                                                                            |
| ----------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `usedBytes`             | Bytes included after applying the database limit.                                                                                                                                  |
| `maxBytes`              | Maximum bytes allowed for the scope.                                                                                                                                               |
| `unboundedBytes`        | Bytes the scope would use without the database limit.                                                                                                                              |
| `includedDocumentCount` | Documents included in the compiled database.                                                                                                                                       |
| `omittedDocumentCount`  | Documents omitted from the compiled database.                                                                                                                                      |
| `omissionReason`        | Why documents were omitted: `size` or `unavailable`.                                                                                                                               |
| `truncated`             | Whether the compiled database omitted content.                                                                                                                                     |
| `queryIssues`           | Structured nonfatal query warnings with a stable code and exact query path.                                                                                                        |
| `queryWarnings`         | Deprecated plain-text query warnings retained for compatibility.                                                                                                                   |
| `issues`                | All content errors and warnings found in files matched by the current query. Each diagnostic identifies its phase, code, message, asset, path, and source location when available. |
| `issueCount`            | Total errors and warnings in `issues`.                                                                                                                                             |
| `issuesTruncated`       | Always `false`; retained for response compatibility.                                                                                                                               |
| `artifacts`             | Optional query and merged compiled artifacts used by detailed Builder diagnostics.                                                                                                 |
| `unresolved`            | Optional query result before document references are resolved. It helps inspect the authored `$ref` values behind resolved output.                                                 |

If the merged database approaches its limit, remove duplicate reachable resources first. Then remove unused output fields or narrow the candidate documents. Prefer `markdown-body-ref` over embedded `full` content for Markdown or MDX articles.

## Document references

A document reference is an exact object with one string field:

```json
{ "$ref": "<relative-path>[#<fragment>]" }
```

Markdown and MDX references can appear in YAML frontmatter. JSON references can appear anywhere in the document. Any format can reference Markdown, MDX, or JSON. References do not run inside a Markdown or MDX body.

| Reference                           | Inserted value                                       |
| ----------------------------------- | ---------------------------------------------------- |
| `../authors/ada.json`               | The complete JSON value.                             |
| `../authors/ada.json#/profile/name` | The value at JSON Pointer `/profile/name`.           |
| `../authors/ada.md`                 | The complete Markdown source, including frontmatter. |
| `../authors/ada.md#frontmatter`     | The Markdown frontmatter object.                     |
| `../authors/ada.md#body`            | The Markdown body without frontmatter.               |

Resolve paths relative to the file containing the reference. JSON Pointer uses `~1` for `/` and `~0` for `~` in property names. URI-encode filename characters that have URL syntax, such as `%23` for `#`. Missing files, invalid fragments, and reference cycles fail instead of returning partial data.

## Query limits

| Limit                          | Value   |
| ------------------------------ | ------- |
| Query request                  | 512 KiB |
| Filter conditions              | 32      |
| Filter nesting depth           | 8       |
| Sort fields                    | 8       |
| Selected output fields         | 256     |
| Field path depth               | 9       |
| Default result count           | 20      |
| Maximum result count           | 1000    |
| Candidate documents            | 1000    |
| Documents in a reference graph | 2000    |
| Serialized query result        | 16 MiB  |
| Published content database     | 500 KiB |

## Content limits

| Limit                           | Value   |
| ------------------------------- | ------- |
| Markdown/MDX frontmatter        | 64 KiB  |
| Frontmatter nesting depth       | 8       |
| Frontmatter fields              | 256     |
| Frontmatter string              | 16 KiB  |
| JSON file                       | 1 MiB   |
| JSON nesting depth              | 8       |
| JSON fields                     | 256     |
| JSON string                     | 16 KiB  |
| Indexed properties per document | 64 KiB  |
| Generated excerpt               | 2 KiB   |
| MDX nesting depth               | 100     |
| MDX nodes                       | 20000   |
| MDX properties                  | 4000    |
| Loaded file                     | 1 MiB   |
| Loaded content per query        | 2 MiB   |
| Loaded files per query          | 20      |
| Loaded range                    | 256 KiB |
| Concurrent content reads        | 8       |

## Related

* [Content Engine](/university/foundations/content-engine) – Build a file-based blog in the visual editor
* [Assets](/university/foundations/assets) – Create and organize project files
* [Webstudio MCP](/university/mcp) – Let an AI agent build and inspect a project
* [Variables](/university/foundations/variables) – Define resources and understand their scope
* [Collection](/university/core-components/collection) – Render a query result as repeated content


# SEO settings

Webstudio provides comprehensive SEO features.

{% embed url="<https://youtu.be/8aNpE5JYn5g>" %}

## ✅ SEO Features

* Meta title
* Meta description
* Open graph
* Semantic tags
* Image alt text
* All meta fields
* Sitemap
* Custom sitemap
* No index
* Default canonical with the ability to [customize it](/university/core-components/head-slot)
* Customizable head (global and [per page](/university/core-components/head-slot))
* Auto exclude no index from sitemap
* Robots.txt
* SSL
* 301 redirects
* 302 redirects
* Site
* WebSite structured data
* Href lang tag
* Auto image conversion (performance)
* Auto image compression (performance)
* Auto set image width and height (CLS)
* Image lazy & eager loading options (performance and LCP)
* Aria labels
* Link rel
* Favicon
* All attributes

## 🌐 Global site settings

In '[Project settings](/university/foundations/project-settings)' under the Webstudio menu, you'll find essential settings for your entire project:

* **Site Name**: Used to output WebSite structured data to clearly define your website's identity.
* **Favicon**: Output your logo in search engines, browser tabs, and more.
* **Custom Header Code**: Global field to output scripts in the head. For modifying the head on a per-page basis, see [Head Slot](/university/core-components/head-slot).

These settings ensure uniformity and brand coherence across all pages of your website.

## 🔍 Individual page SEO settings

Fine-tune SEO settings for each page through the Pages panel. See [Page settings](/university/foundations/page-settings) for a full reference of all available fields, including title, description, social image, custom meta tags, and more.

{% hint style="info" %}
The social image you add to your homepage will be used as the cover image for the project on the dashboard.
{% endhint %}

## 🐦 Twitter card customization

To customize how your content appears when shared on Twitter/X:

1. Go to **Page Settings → Custom Meta Tags**
2. Add a meta tag with:
   * Property: `twitter:card`
   * Content: `summary_large_image` (for large image cards)

## 🔧 Debugging social sharing

Social platforms cache sharing previews. To clear caches and test your changes:

* **Facebook**: [Sharing Debugger](https://developers.facebook.com/tools/debug/)
* **Twitter/X**: [Card Validator](https://cards-dev.twitter.com/validator)

{% hint style="info" %}
When you publish to a custom domain, staging and webstudio domains are automatically de-indexed from search engines.
{% endhint %}

## Related

* [Page settings](/university/foundations/page-settings) – Per-page SEO fields: title, description, social image, custom meta tags
* [Project settings](/university/foundations/project-settings) – Configure site name, favicon, and global settings
* [Head Slot](/university/core-components/head-slot) – Add custom meta tags and scripts per page
* [Publishing & custom domains](/university/foundations/publishing-and-custom-domains) – Deploy your site with custom domains
* [CMS](/university/foundations/cms) – Create dynamic pages with SEO-optimized content


# Performance

Achieve perfect Lighthouse scores with Webstudio's built-in optimizations and best practices.

Webstudio sites are fast by default. This guide covers what's automatic and what you control.

## Measuring Performance: Core Web Vitals vs PageSpeed

[PageSpeed Insights](https://pagespeed.web.dev/) runs a single Lighthouse test from one server location. It's useful for spotting issues, but a single synthetic test doesn't reflect real-world performance.

**Core Web Vitals** (LCP, CLS, INP) are what actually matter—they measure real user experiences across all devices, networks, and locations. A site can score 100 on PageSpeed but still have poor Core Web Vitals if real users experience slow loads.

Check your Core Web Vitals in:

* [Google Search Console](https://search.google.com/search-console) – Real field data from Chrome users
* [PageSpeed Insights](https://pagespeed.web.dev/) – The "Field Data" section at the top (if available)

Focus on passing Core Web Vitals thresholds, not chasing a perfect Lighthouse score.

## What Webstudio Does Automatically

### Image Optimization

The [Image component](/university/core-components/image) automatically:

* **Converts images to WebP/AVIF** – Modern formats with better compression
* **Compresses images** – Reduces file size without visible quality loss
* **Generates responsive sizes** – Serves appropriately sized images based on screen width
* **Sets width and height attributes** – Prevents Cumulative Layout Shift (CLS)

You upload a high-quality image once, and visitors receive an optimized version.

{% hint style="info" %}
These optimizations only apply to the **Image component**. Images added via HTML Embed, Content Embed, or other methods won't receive automatic optimization.
{% endhint %}

### Cloudflare Edge Deployment

Published sites deploy to [Cloudflare Workers](https://webstudio.is/cloudflare-website-builder) across 320+ global locations. Your site loads from the server closest to each visitor.

### Atomic CSS

Webstudio generates minimal CSS. Styles are deduplicated and optimized, resulting in smaller file sizes than traditional class-based approaches.

### Server-Side Rendering (SSR)

Pages are rendered on the server and delivered as complete HTML. This improves:

* **First Contentful Paint (FCP)** – Content appears faster
* **SEO** – Search engines receive fully rendered pages
* **Time to Interactive** – Less JavaScript to parse

## What You Control

### Image Loading: Lazy vs Eager

In the Image component settings:

| Setting   | Use When                                            |
| --------- | --------------------------------------------------- |
| **Eager** | Above-the-fold images (hero, logo). Improves LCP.   |
| **Lazy**  | Below-the-fold images. Defers loading until needed. |

{% hint style="info" %}
Set your hero image and logo to **eager**. Everything else can be **lazy**.
{% endhint %}

### Font Loading

Upload fonts directly to Webstudio instead of using external services like Google Fonts.

**Why it matters:**

* Eliminates third-party requests
* No additional SSL/TLS handshake to external domains
* Fonts load from the same edge location as your site
* No render-blocking external stylesheets
* No third-party tracking (Google Fonts logs visitor IP addresses)

See [How to Use Custom Fonts](/university/how-tos/how-to-use-custom-fonts) for setup instructions.

{% hint style="danger" %}
**Common font mistakes:**

* **Large font files** – Use `.woff2` format for best compression
* **Too many font weights** – Only upload weights you actually use (e.g., 400 and 700, not 100-900)
* **Unused fonts** – Uploaded fonts are prefetched even if not used on the page. Delete fonts you don't need from assets.
* **Too many font families** – Each family adds load time. Stick to 1-2 families.
  {% endhint %}

#### Subsetting Fonts (Removing Unused Characters)

Most fonts include characters for multiple languages (Latin, Cyrillic, Greek, Vietnamese, etc.). If your site only uses Latin characters, you can dramatically reduce font file size by downloading a subsetted version.

**Using Google Fonts API to get Latin-only fonts:**

1. Go to [Google Fonts](https://fonts.google.com/) and select your font
2. Modify the embed URL to request only Latin characters:

```
https://fonts.googleapis.com/css2?family=Inter:wght@400;700&display=swap&subset=latin
```

3. Open that URL in your browser – it returns CSS with links to `.woff2` files
4. Download those `.woff2` files and upload them to Webstudio

**Example size reduction:**

| Font           | Full    | Latin-only |
| -------------- | ------- | ---------- |
| Inter Regular  | \~100KB | \~20KB     |
| Roboto Regular | \~150KB | \~25KB     |

{% hint style="info" %}
For even smaller files, use the `text=` parameter to include only specific characters: `https://fonts.googleapis.com/css2?family=Inter&text=ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789`
{% endhint %}

**Alternative tools for subsetting:**

* [glyphhanger](https://github.com/zachleat/glyphhanger) – Subset fonts based on actual characters used on your site
* [FontSquirrel Webfont Generator](https://www.fontsquirrel.com/tools/webfont-generator) – Upload a font and select subsets
* [Transfonter](https://transfonter.org/) – Online font converter with subsetting options

### Third-Party Scripts

Add scripts in [Project settings](/university/foundations/project-settings) or [Head Slot](/university/core-components/head-slot).

**Always use `defer` or `async`** to prevent render-blocking:

| Attribute       | Behavior                                                                            | Use When                                             |
| --------------- | ----------------------------------------------------------------------------------- | ---------------------------------------------------- |
| `defer`         | Downloads during HTML parsing, executes after parsing completes, maintains order    | Most scripts (analytics, widgets, libraries)         |
| `async`         | Downloads during HTML parsing, executes immediately when ready, no guaranteed order | Independent scripts that don't rely on other scripts |
| `type="module"` | Automatically deferred                                                              | ES modules                                           |
| *(none)*        | Blocks HTML parsing until downloaded and executed                                   | ❌ Avoid unless absolutely necessary                  |

{% hint style="info" %}
When in doubt, use `defer`. It's safer than `async` because scripts execute in order.
{% endhint %}

**Best practices:**

* **Always add `defer`** – Never add scripts without `defer` or `async`
* **Minimize third-party dependencies** – Each external script adds latency
* **Load scripts from fast CDNs** – Use well-known CDNs that are likely already cached

## Measuring Performance

### Lighthouse

Run Lighthouse in Chrome DevTools (F12 → Lighthouse tab) or use [PageSpeed Insights](https://pagespeed.web.dev/).

**Key metrics:**

| Metric                             | Target  | What It Measures              |
| ---------------------------------- | ------- | ----------------------------- |
| **LCP** (Largest Contentful Paint) | < 2.5s  | When main content is visible  |
| **FID** (First Input Delay)        | < 100ms | Responsiveness to interaction |
| **CLS** (Cumulative Layout Shift)  | < 0.1   | Visual stability              |
| **FCP** (First Contentful Paint)   | < 1.8s  | When first content appears    |
| **TTFB** (Time to First Byte)      | < 800ms | Server response time          |

### Common Issues and Fixes

| Issue                     | Cause                        | Fix                                                          |
| ------------------------- | ---------------------------- | ------------------------------------------------------------ |
| Poor LCP                  | Hero image loads slowly      | Set image to eager, ensure it's not unnecessarily large      |
| High CLS                  | Images without dimensions    | Webstudio sets dimensions automatically; check custom embeds |
| Slow TTFB                 | Heavy API calls on page load | Optimize CMS queries, reduce API calls                       |
| Render-blocking resources | External fonts or scripts    | Use uploaded fonts, defer scripts                            |

## CMS Performance

When fetching data from external APIs:

* **Minimize API calls per page** – Combine requests where possible
* **Use caching** – Configure your CMS for appropriate cache headers
* **Avoid synchronous chains** – Multiple dependent API calls slow page load

See [CMS](/university/foundations/cms) for configuration details.

## Self-Hosted Performance

When [self-hosting](/university/self-hosting), performance depends on your hosting provider. Webstudio Cloud uses Cloudflare's global edge network; self-hosted deployments may have different characteristics.

For optimal self-hosted performance:

* Choose a hosting provider with edge locations (Vercel, Netlify, Cloudflare Pages)
* Enable caching
* Use a CDN for assets

## Related

* [Image](/university/core-components/image) – Image component settings
* [How to Use Custom Fonts](/university/how-tos/how-to-use-custom-fonts) – Font upload guide
* [SEO settings](/university/foundations/seo-settings) – Related optimization settings
* [CMS](/university/foundations/cms) – External data fetching
* [Cloudflare Website Builder](https://webstudio.is/cloudflare-website-builder) – How Webstudio uses Cloudflare


# Shortcuts

The list of keyboard shortcuts to build in Webstudio.

## Operating system notes

### Windows

* Replace `⌘` (`command`) with `control` (`Ctrl`).

### Mac

* On Mac, `alt` and `option` are the same key (`⌥`).

## **General**

* Undo: `⌘ + z`
* Redo: `⌘ + shift + z`
* Deselect/abort: `esc`
* Open Commands & search: `⌘ + k`
  * See [Commands & search](/university/foundations/commands-and-search) to perform the following actions:
    * Unwrap
    * Wrap In Box
    * Wrap In Link
* Hide UI: `⌘ + \`

## **Sidebar Left**

* Copy: `⌘ + c`
* Cut: `⌘ + x`
* Paste: `⌘ + v`
* Duplicate: `⌘ + d`
* Open Navigator: `z`
* Open Add Components: `a`
* Edit Component: `enter`
* Rename instance label: `⌘ + e`
* Expand Navigator item: `right arrow`
* Collapse Navigator item: `left arrow`
* Expand all Navigator item children: `alt + click`
* Add or remove an item from the selection: `⌘ + click`
* Select a range of items: `shift + click`
* Extend the selection: `shift + up/down arrow`
* Select all sibling items: `⌘ + a`
* Move selection before or after a sibling: `Ctrl + up/down arrow`
* Move selection out of its parent: `Ctrl + left arrow`
* Move selection into the previous sibling: `Ctrl + right arrow`

## **Pages Panel**

* Copy selected page, folder, or page template: `⌘ + c`
* Paste copied page, folder, or page template: `⌘ + v`
* Duplicate selected page, folder, or page template: `⌘ + d`
* Delete selected page, folder, or page template: `backspace`

## **Style Panel**

* Open Style Panel: `s`
* Focus Style Sources: `⌘ + enter`
* Reset style: `alt + click`
* Reset style for all sides: `shift + alt + click`
* Edit spacing on all sides (via drag): `shift + drag`
* Edit spacing on all sides (via one input): add value then `shift + enter`
* Edit spacing on two sides (via drag): `alt + drag`
* Edit spacing on two sides (via one input): add value then `alt + enter`
* Increment value by 10: `shift + up/down arrow`
* Increment value by .1: `alt + up/down arrow`
* View the computed CSS variable value in the tooltip: `hold alt`

## **Settings Panel**

* Open Settings Panel: `d`

## **Help**

* Open Shortcuts Help Dialog: `⌘ + /` – View all available keyboard shortcuts in a searchable dialog

## **Top Bar**

* Open the current page's settings: `alt + click page`
* Switch [modes](/university/foundations/modes)
  * Design: `⌘ + shift + d`
  * Content: `⌘ + shift + c`
  * Preview: `⌘ + shift + p`
* Switch between current mode and Preview: `⌘ + shift + c|d`
  * For example, if you are in Design mode, you can press `⌘ + shift + d` and it’ll toggle Preview. You can use the same shortcut to exit Preview.
* Switch breakpoints: `numbers` (e.g., `1` switches to the first one)

## Related

* [Commands & search](/university/foundations/commands-and-search) – Navigate and perform actions with the command palette
* [Anatomy of the Webstudio builder](/university/foundations/anatomy-of-the-webstudio-builder) – Overview of the Builder interface
* [Modes](/university/foundations/modes) – Switch between Design, Content, and Preview modes
* [Design tokens](/university/foundations/design-tokens) – Create and manage reusable styles


# Page settings

Configure per-page settings such as path, SEO, authentication, redirects, and metadata.

Page settings control how an individual page behaves — its URL, SEO metadata, authentication, status code, redirect, and more. Open Page Settings by clicking the gear icon next to any page in the Pages panel.

<picture><source srcset="/files/uKk4lsOt1QTZw8gBWfli" media="(prefers-color-scheme: dark)"><img src="https://330243581-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTep4EGj6hSC54ozHujyK%2Fuploads%2Fgit-blob-7265f667ae15ae22f2ace49f7df09c6c58ab78b5%2Fpage-settings-dialog-light.png?alt=media" alt="Page settings for the Home page with general and authentication fields above a search preview showing the Webstudio W favicon"></picture>

## Page name

The name displayed in the Pages panel in the builder. It does not affect the URL or any output — it's purely for organizing pages in the editor.

When creating a page from a [page template](/university/foundations/page-templates), the page name is pre-filled from the template and can be adjusted before the page is created.

## Draft pages

Mark an unfinished page as a draft to keep working on it without including it in the next deployment. Open the page settings menu and choose **Mark as draft**. The Pages panel prefixes its display name with `[Draft]` without changing the stored page name.

Draft pages remain editable, previewable, linkable, copyable, and duplicable in the Builder. They continue to reserve their paths, but Webstudio excludes them from generated routes, public page collections, sitemaps, staging, and production builds.

Choose **Stage for publish** from the same menu when the page is ready. This removes its draft state so it can be included in a future deployment; it does not publish the site immediately.

The home page and the `/*` catch-all page cannot be drafts. A draft must be staged before you can make it the home page.

Connected agents can create and edit draft pages and include them in private generated previews for visual audits. This does not expose the drafts on staging or production.

## Path

The URL path for this page, e.g. `/about` or `/blog/:slug`.

### Path syntax

See [Path patterns](/university/foundations/path-patterns#pattern-syntax) for static paths, dynamic segments, optional segments, wildcards, and matching examples. A dynamic segment such as `:slug` makes the page a [Dynamic Page](/university/foundations/cms#dynamic-pages).

In Page settings, the home page has an empty path. Other page paths start with `/`.

Webstudio Cloud sites and exported JavaScript applications permanently redirect a trailing-slash URL such as `/about/` to the page path `/about` and preserve its query string. For static exports, the hosting platform controls [trailing-slash behavior](/university/self-hosting#static-site-url-behavior).

## Status code

The HTTP status code returned when this page is requested. Defaults to `200`.

Can be bound to an expression to return a different code conditionally — most commonly `404` when a dynamic page receives a parameter that doesn't match any CMS record. See [Handling dynamic 404s](/university/foundations/cms#handling-dynamic-404s) for details.

## Redirect

Redirects all requests for this page's path to another path. Useful for retired URLs or reorganized site structure.

Enter a path such as `/new-page`. This performs a `301` permanent redirect. Leave empty if no redirect is needed.

The redirect field supports expressions, making it dynamic. For example, on a dynamic page you can redirect to your 404 page when no CMS data is found. See [Alternative: redirect instead of showing 404 content](/university/foundations/cms#alternative-redirect-instead-of-showing-404-content) for details.

## Language

Sets the `lang` attribute on the `<html>` element for this page, e.g. `en`, `fr`, `de`. Used by browsers, screen readers, and search engines to identify the page language.

Can be bound to an expression — for example using a URL parameter — to serve pages in different languages from a single Dynamic Page.

## Document type

Controls the response format for the page.

| Type     | Use for                                                                                                                         |
| -------- | ------------------------------------------------------------------------------------------------------------------------------- |
| **HTML** | Regular web pages built visually on the canvas                                                                                  |
| **XML**  | XML-based output such as sitemaps and RSS feeds. See the [XML Node component](/university/core-components/xml-node) for details |
| **TEXT** | Plain text output such as `robots.txt`, `ads.txt`, `security.txt`, verification files, or generated text responses              |

HTML is the default document type. XML and TEXT pages cannot be set as the home page.

## Plain text pages

Use the **TEXT** document type when a route needs to serve plain text instead of an HTML document. Text pages return only the content from the page's **Content** section with a `text/plain` response.

Common uses include:

* `robots.txt`
* `ads.txt`
* `/.well-known/security.txt`
* Domain or service verification files
* Dynamic text generated from Resources and expressions

To create a plain text page:

1. Create or open a page in the Pages panel
2. Set the **Path**, such as `/robots.txt` or `/.well-known/security.txt`, using the same [path syntax](#path-syntax) as other pages
3. Set **Document type** to **TEXT**
4. Open the **Content** section
5. Enter the plain text, or bind the field to an expression
6. Publish the site

<figure><img src="https://330243581-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTep4EGj6hSC54ozHujyK%2Fuploads%2Fgit-blob-7a8da01fffad7a18c7a9c85df1daea2ddf0e4abd%2Fpage-settings-plain-text-page.png?alt=media" alt="Page Settings showing Document type set to TEXT and the Content section with a plain text field"><figcaption><p>Plain text page settings</p></figcaption></figure>

Plain text pages can still use Page Settings fields such as Status code, Redirect, Authentication, and Dynamic data. SEO, social image, and custom metadata fields apply only to HTML pages, so they are hidden when the document type is TEXT.

Plain text pages are not included in the generated sitemap because they are not HTML pages.

## Authentication

Use Authentication to require HTTP Basic Auth credentials before visitors can load this page on custom domains.

1. Open the page's settings from the Pages panel
2. Open the **Authentication** section
3. Enable **Require login and password**
4. Enter a login and password
5. Publish the site

Page authentication is useful for private previews, client-only pages, internal pages, or temporary gated content.

<figure><img src="https://330243581-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTep4EGj6hSC54ozHujyK%2Fuploads%2Fgit-blob-dd1ef8c945477304afdf6a91864ba80b817c2056%2Fpage-settings-authentication-enabled.png?alt=media" alt="Page Settings Authentication section enabled with Login and Password fields"><figcaption><p>Page authentication</p></figcaption></figure>

{% hint style="info" %}
Authentication applies to protected pages on custom domains. Staging domains have their own built-in password protection, described in [Publishing & custom domains](/university/foundations/publishing-and-custom-domains#staging-password-protection).
{% endhint %}

{% hint style="warning" %}
Authentication is a Pro feature for custom domains. You can publish to staging for free, but publishing authentication to custom domains requires a plan that includes it.
{% endhint %}

Login and password rules:

* Login is required
* Password is required
* Login cannot contain `:`
* Login and password cannot contain whitespace
* Password can contain `:`

To protect multiple routes, dynamic paths, or wildcard sections of the site, use [Project settings](/university/foundations/project-settings#authentication).

## Search

SEO settings that control how the page appears in search engine results.

<picture><source srcset="/files/3uyrUKUx949c6BRtdjjx" media="(prefers-color-scheme: dark)"><img src="https://330243581-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTep4EGj6hSC54ozHujyK%2Fuploads%2Fgit-blob-3ec0b8a0c6b5be2fa78e2b558f8c48061cae68ea%2Fpage-settings-seo-light.png?alt=media" alt="Page settings Search section with the Webstudio W favicon in the result preview, the Blog title and description, search visibility, and en-US language fields"></picture>

### Title

The `<title>` tag and the headline shown in search results. Should clearly describe the page content. Can be bound to a CMS variable on dynamic pages.

### Description

The meta description shown as the snippet in search results. Does not affect rankings directly but influences click-through rate. Can be bound to a CMS variable.

### Exclude from search

Adds a `noindex` directive to the page, preventing search engines from indexing it.

## Social image

The Open Graph image displayed when the page is shared on social media (Facebook, X, LinkedIn, etc.). You can either upload an image or bind a URL expression to a dynamic image from your CMS.

<picture><source srcset="/files/fAqZp29ih3eTTtwjHuj7" media="(prefers-color-scheme: dark)"><img src="https://330243581-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTep4EGj6hSC54ozHujyK%2Fuploads%2Fgit-blob-185e2f1ed673df9165c2f774415abcb8a3294cf6%2Fpage-settings-social-light.png?alt=media" alt="Page settings Social image section with a selected blue and purple glass wave image and its social sharing preview"></picture>

## Custom metadata

Add arbitrary `<meta>` tags to the page's `<head>`. Each entry has a **property** (the meta tag's `name` or `property` attribute) and a **content** value, both of which support expressions.

<picture><source srcset="/files/UUhWsuGhcJhkvAdNLkV1" media="(prefers-color-scheme: dark)"><img src="https://330243581-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTep4EGj6hSC54ozHujyK%2Fuploads%2Fgit-blob-bb9ea997ac7eae8643d1ea31cb4e67741eb7a36e%2Fpage-settings-custom-metadata-light.png?alt=media" alt="Page settings with a blue and purple wave image in the Social sharing preview above empty Custom metadata fields"></picture>

Use this for meta tags not covered by the fields above, such as `og:type`, `twitter:card`, or any custom meta needed by third-party integrations.

## Dynamic data

Variables and Resources defined on the page are scoped to that page and are available to bind to components and Page Settings fields. Define a [Resource variable](/university/foundations/variables#resource) here to fetch CMS data and then bind it to the Title, Description, Status Code, and other fields above.

## Content mode access

In Content mode, editors can update the page settings that affect editable content and share previews:

* Page name
* Static page path
* Search title and description
* Exclude from search
* Language
* Social image
* Custom metadata

Content editors cannot create dynamic paths such as `/blog/:slug`, wildcard paths such as `/docs/*`, or external URL paths. Redirects, status codes, document type, authentication, dynamic data, and other structural settings remain available only in Design mode.

## Related

* [CMS](/university/foundations/cms) – Connect to a CMS and use dynamic data on pages
* [Dynamic 404 handling](/university/foundations/cms#handling-dynamic-404s) – Return 404 when CMS data is missing
* [Project settings](/university/foundations/project-settings) – Site-wide settings such as favicon, custom code, redirects, and route authentication
* [Page templates](/university/foundations/page-templates) – Create reusable blueprints for new pages
* [Publishing & custom domains](/university/foundations/publishing-and-custom-domains) – Publish protected pages to custom domains
* [Variables](/university/foundations/variables) – Define and use variables on pages
* [Expression editor](/university/foundations/expression-editor) – Bind expressions to Page Settings fields
* [XML Node](/university/core-components/xml-node) – Build XML pages such as sitemaps
* [Custom 404 page](/university/how-tos/how-to-make-a-custom-404-page) – Create a custom 404 page


# Page templates

Create reusable page blueprints inside a project.

Page templates are reusable page blueprints stored inside a project. They help designers create consistent page structures that can be turned into new pages later.

Use page templates when you want to:

* Start new pages from a consistent layout
* Give content editors a safe way to create new pages
* Reuse SEO, social image, language, and custom metadata defaults
* Keep page creation consistent across a client or team project

{% embed url="<https://youtu.be/ysATesLnuaI>" %}

{% hint style="info" %}
Page templates are different from [Marketplace](/university/marketplace) templates. Marketplace templates are reusable assets you insert from Webstudio or the community. Page templates are private to the current project and are created by the project designer.
{% endhint %}

## How page templates work

Page templates live in the **Page templates** section of the Pages panel.

Templates are not published pages. They do not have paths, do not appear in the sitemap, and do not create public routes. They only become public when someone creates a regular page from the template and publishes that page.

Pages created from a template are independent copies. Updating the template later does not update pages that were already created from it.

## Creating a page template

In Design mode:

1. Open the Pages panel
2. Click the create button
3. Choose **New page template**
4. Enter the template name and page metadata defaults
5. Click **Create template**
6. Design the template on the canvas

Template settings include the same editorial metadata used by pages, such as title, description, search visibility, language, social image, and custom metadata. Templates do not include page paths, redirects, status codes, document type, or authentication because they are not live routes.

## Creating a page from a template

In the **Page templates** section, click the create button on a template row.

Webstudio opens **Create page from template**, pre-filled with values from the template. Review or adjust the page name, path, SEO, social image, and other page settings, then click **Create page**.

Webstudio creates a regular page with a copied component tree, fresh internal IDs, and a unique path based on the page name.

## Editing and managing templates

Designers can:

* Select a template to edit it on the canvas
* Open template settings
* Copy a template and paste it into another project
* Duplicate a template
* Delete a template
* Reorder templates in the Page templates section

Right-click a template row to copy, duplicate, or delete it.

## Copying pages, folders, and templates

In Design mode, the Pages panel supports copy and paste for pages, folders, and page templates.

Use this when you want to:

* Move a finished page structure between projects
* Copy a group of pages organized in a folder
* Reuse a page template in another project
* Create a backup copy before making structural changes

Right-click a page or folder and choose **Copy**, then right-click the destination folder and choose **Paste**. You can also use `⌘ + c` and `⌘ + v` when the page, folder, or template is selected. On Windows, use `Ctrl` instead of `⌘`.

When pasted, Webstudio creates new internal IDs and automatically adjusts names, paths, folder slugs, tokens, assets, variables, and styles as needed to fit the destination project.

{% hint style="info" %}
When copying between separate Webstudio deployments, the destination downloads referenced asset files from the source before inserting the content. The source deployment must remain publicly reachable until the paste finishes. Use the [CLI](/university/cli) to sync and import the complete project when moving an entire site.
{% endhint %}

## Content mode

Content editors can create pages from existing page templates when they have editing access. This gives editors a controlled page creation workflow without giving them full design control.

Editors cannot create or edit the templates themselves. When creating a page from a template, editors can only change fields that are safe for content editing. Dynamic, data-bound, or structural settings remain controlled by the designer.

In Content mode, editors can create regular static pages from templates and edit safe page settings such as page name, path, title, description, search visibility, language, social image, and custom metadata. Dynamic paths, redirects, status codes, document type, authentication, and template management remain Design-mode controls.

## Related

* [Page settings](/university/foundations/page-settings) – Configure page paths, SEO, authentication, and metadata
* [Modes](/university/foundations/modes) – Understand Design and Content mode permissions
* [Marketplace](/university/marketplace) – Insert community and Webstudio templates
* [Reusability & maintainability](/university/foundations/reusability) – Choose the right reuse tool


# Project settings

Set project-wide configuration, including redirects, authentication, and publishing options.

Project settings are located in the top left by clicking the Webstudio logo > Project settings.

<picture><source srcset="/files/LayT4QEcvamD7TJf9xKY" media="(prefers-color-scheme: dark)"><img src="https://330243581-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTep4EGj6hSC54ozHujyK%2Fuploads%2Fgit-blob-5ea7982dfde9f73a6a8e772d191065096ca1231a%2Fproject-settings-menu-light.png?alt=media" alt="Builder menu with Project settings near the top"></picture>

## General

* **Site Name** – Used to output [WebSite structured data](https://schema.org/WebSite) to clearly define your website's identity.
* **Favicon** – Output your logo in search engines, browser tabs, and more.
* **Custom Code** – Global field to output scripts in the head. Custom Code is often used to add analytics scripts such as Google Analytics, PostHog, Plausible, and any other scripts/code you want to output on every page. Please note that this code does *not* output in the Builder, so your scripts aren't tracking Builder page views. For outputting a script in the body on every page, use a [Slot](/university/core-components/slot). For example, add [HTML Embed(s)](/university/core-components/html-embed) to your Footer Slot so that it outputs on every page.
* **Compiler** – Atomic CSS reduces the CSS file size by \~70% in many cases. See more below.

## Emails

Configure recipients and Reply to here. Recipients are comma-separated mailboxes such as `team@example.com` or `Name <address@example.com>`; quote a display name that contains a comma. When the list is empty, new email resources fall back to the project owner's address. An email resource can inherit these recipients or use its own fixed recipient list; submitted Form values cannot choose team recipients. Set the subject and body on each email resource.

To send visitor email, create an email resource, select **Visitor** as its recipient, choose a named email input on the Form, and add the resource to the Form's Action. Its body starts empty. Webstudio always prepends a fixed confirmation sentence with the site's URL, followed by any body configured in the resource. The body can use normal resource bindings. When a Form includes file inputs, submitted files are attached to the outgoing email resource message by default. Turn off ‘Attach submitted files’ to omit them. This action runs alongside other actions, and its failure is reported without reversing an otherwise successful Form submission.

Reply to accepts one mailbox, with or without a display name: `Acme Support <support@example.com>` or `support@example.com`. Its address is for replies, and its optional name is the display name. Webstudio's authenticated From address stays fixed. Each email resource inherits this setting unless it has its own Reply to override. The resource override is empty by default.

Each email from a Form submission includes an `X-Message-ID` header. Email actions from the same submission share the header value, and a retry keeps it. Email subjects stay as configured.

An email resource without a custom body includes submitted Form fields and browser information by default when used on a Form. The automatic text includes submitted hidden fields, but excludes password inputs and Webstudio's internal bot fields. Set the resource body to customize the message, including an explicitly empty body. To use `formData` or `browserInfo` in a resource expression, define the resource on the Form; resources on an ancestor or global root cannot bind that Form's data.

Email resources inherit project recipients and Reply to unless overridden. Republish to update the settings included in a published site. On Webstudio Cloud, published email resources use the private Cloudflare email service when the `EMAIL_SERVICE` binding is configured. Saving settings or publishing does not itself send email; a visitor must submit the Form. Delivery fails explicitly if the service is unavailable. Self-hosted sites need a custom server-side email integration. The existing Contact email recipient setting continues to serve deprecated Webhook Forms.

## Publishing

### Atomic CSS

<picture><source srcset="/files/xwkgUnzQ4EhO3f7TuWNR" media="(prefers-color-scheme: dark)"><img src="https://330243581-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTep4EGj6hSC54ozHujyK%2Fuploads%2Fgit-blob-789958a102e5a4eebc2d1d7cb610698a82208e93%2Fatomic-css-setting-light.png?alt=media" alt="Project settings Publishing section with Generate atomic CSS when publishing enabled"></picture>

When enabled, the class and CSS structure under the hood contains one style per class. This algorithm allows classes to be reused, significantly reducing the amount of CSS, ultimately leading to a faster-loading website.

For example, in the UI, you may create a Token called “Card” and give it a background color, padding, and a border-radius.

With atomic CSS *enabled*, it will output like this:

```css
  .ceszdr {
    background-color: #fff;
  }
  .c1jykks9 {
    border-top-left-radius: 1rem;
  }
  .c31r7mo {
    border-top-right-radius: 1rem;
  }
  .cywm6f1 {
    border-bottom-left-radius: 1rem;
  }
  .c1q77ydo {
    border-bottom-right-radius: 1rem;
  }
  .cu6yur2 {
    padding: 20px;
  }
```

Even though this seems to take up more space, its benefits become apparent as you style more parts of the website. Now, anytime you add 20px of padding, it’ll automatically reuse the `cu6yur2` class. Generally speaking, there are somewhat of a finite amount of styles you’ll use on your website, so as the website gets bigger, the CSS file does *not* grow proportionally – in fact, its growth slows down as the site gets bigger as it doesn’t need to continue creating classes for styles you’ve already used. Pretty neat.

#### The data

Let’s quantify the CSS file size savings when using atomic CSS on Webstudio.

* 4-page brochure website
  * With atomic: 21 KB
  * Without atomic: 75 KB
* 23-page SaaS website
  * With atomic: 23 KB
  * Without atomic: 83 KB

**In both cases, enabling atomic CSS reduces the file size by around 72%!**

#### Disabling atomic CSS

Even though atomic CSS improves website performance by reducing the CSS file size, there are use cases to disable it.

When exporting your Project to [self-host](/university/self-hosting) it, you *may* want to modify CSS and classes *outside* of Webstudio. By disabling atomic CSS, your classes are human-readable, instead of using an optimized algorithm.

{% hint style="info" %}
If you need classes to target but want to enable atomic CSS, you can add classes in the settings panel. These will always output exactly as they are entered. Watch [this short video](https://www.youtube.com/watch?v=_1QSWHOtk08) to learn more.
{% endhint %}

When atomic CSS is disabled, your components will have two classes:

1. A default class, such as `w-box`
2. Your [Tokens](/university/foundations/design-tokens) and Local Styles merged into another class

The same example from above looks like this when atomic is *disabled*:

```css
.w-box-1 {
    background-color: rgb(255, 255, 255);
    border-radius: 1rem;
    padding: 20px;
}
```

## Redirects

Redirects old URLs to new ones so that you don’t lose any traffic or search engine rankings. 301 and 302 status codes are available.

## Authentication

Use Authentication to require HTTP Basic Auth credentials for one or more routes on custom domains.

This is useful when you want to protect:

* A group of pages, such as `/private/*`
* Dynamic routes, such as `/blog/:slug`
* The home page, using `/`
* A route that does not map cleanly to one page setting

To add a protected route:

1. Open **Project settings**
2. Go to **Authentication**
3. Enter a route, such as `/private` or `/docs/*`, plus a login and password
4. Click **Add**
5. Publish the site

Routes use the shared [path pattern syntax](/university/foundations/path-patterns). The guide explains how static paths, parameters, and wildcards match URLs.

<picture><source srcset="/files/QBDGPk17y6Sr9MWBWYIb" media="(prefers-color-scheme: dark)"><img src="https://330243581-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTep4EGj6hSC54ozHujyK%2Fuploads%2Fgit-blob-7d2f94b78561acfbff7807c04e08e9d005d1cf11%2Fproject-settings-authentication-light.png?alt=media" alt="Project settings Authentication section with route, login, password, and Add controls"></picture>

Login and password rules:

* Login is required
* Password is required
* Login cannot contain `:`
* Login and password cannot contain whitespace
* Password can contain `:`

If a page has authentication in Page Settings and the same route is also protected in Project Settings, the page setting takes priority for that route.

{% hint style="info" %}
Authentication applies to protected routes on custom domains. Staging domains have their own built-in password protection, described in [Publishing & custom domains](/university/foundations/publishing-and-custom-domains#staging-password-protection).
{% endhint %}

{% hint style="warning" %}
Authentication is a Pro feature for custom domains. You can publish to staging for free, but publishing authentication to custom domains requires a plan that includes it.
{% endhint %}

For a single page, you can also configure authentication directly in [Page settings](/university/foundations/page-settings#authentication).

## Headers

Configure [HTTP response headers](/university/foundations/response-headers) to set browser and CDN behavior for your published site. You can apply a header to every route or only to matching paths. Webstudio Cloud supplies framing and referrer defaults when you have not set your own values; platform-managed headers cannot be changed here.

## Marketplace

You can contribute free or paid templates by creating a Project and submitting it for review. Approved templates will appear in the [Marketplace](/university/marketplace).

For more information, see [Contributing to the Marketplace](/contributing/marketplace).

## Related

* [SEO settings](/university/foundations/seo-settings) – Configure meta tags and social sharing
* [Publishing & custom domains](/university/foundations/publishing-and-custom-domains) – Deploy your site and manage domains
* [Page settings](/university/foundations/page-settings) – Configure authentication for a single page
* [Design tokens](/university/foundations/design-tokens) – Understand atomic CSS output options
* [Head Slot](/university/core-components/head-slot) – Add custom code per page
* [HTML Embed](/university/core-components/html-embed) – Embed analytics and custom scripts


# HTTP response headers

Configure HTTP response headers and route-specific policies for a published site.

HTTP response headers are metadata sent with a page response. Browsers and other clients use them to decide how to handle the response—for example, whether a page can appear inside an iframe, how to handle referrers, or how long to cache content. Headers are sent by the server; they are different from the visible content on your page and from HTML added to the page's `<head>`.

Use **Project settings > Headers** to add response header rules to your published site.

## Add a header rule

1. Open **Project settings** and select **Headers**.
2. Enter a path pattern, a header name, and its value.
3. Select **Add**.
4. Publish your site for the rule to take effect.

To change a rule, remove it with the trash button and add it again with the updated values.

Header names are case-insensitive. Suggestions in the header-name field are common choices, not a complete allowlist; you can enter other valid header names too. Webstudio reserves platform-managed and connection-specific headers, as well as headers that describe response-body framing, such as `Content-Length` and `Transfer-Encoding`.

## Choose which paths receive a header

The path pattern determines which page responses receive the rule. Headers use the same path pattern syntax as project-level Authentication. See [Path patterns](/university/foundations/path-patterns) for the complete syntax, examples, and matching behavior.

Add separate rules to use the same header on different paths. If several matching rules set the same header, the last matching rule in the list wins. A route-specific value replaces the value that would otherwise apply to that response; write the full value you want the browser to receive.

## Webstudio Cloud defaults

On Webstudio Cloud, responses receive these defaults when you have not configured the corresponding header:

| Header                    | Default value                     | Purpose                                              |
| ------------------------- | --------------------------------- | ---------------------------------------------------- |
| `Content-Security-Policy` | `frame-ancestors 'self'`          | Allows pages to be framed by the same site.          |
| `X-Frame-Options`         | `SAMEORIGIN`                      | Legacy framing protection for same-origin embedding. |
| `Referrer-Policy`         | `strict-origin-when-cross-origin` | Limits referrer details sent to another origin.      |

`X-Powered-By`, `X-Content-Type-Options`, and `Strict-Transport-Security` are managed by Webstudio Cloud and cannot be configured in this section.

On Webstudio Cloud, the defaults and custom headers on staging domains are free. Custom header rules on a custom domain require Pro.

## Example: allow a trusted site to frame one page

By default, your pages can be framed by your own site. This helps protect visitors from **clickjacking**: an attacker could place your site inside a hidden or misleading frame and trick a visitor into clicking a control on your site without realizing it.

Sometimes a trusted analytics tool offers an iframe-based heatmap viewer. The viewer needs to display your page in a frame so it can place click data over the page. You can allow that viewer while leaving the rest of your site protected:

1. Add a rule for the page you want the viewer to show, for example `/campaigns/spring`.
2. Set the header name to `Content-Security-Policy`.
3. Set its value to `frame-ancestors 'self' https://analytics.example.com`, replacing the example origin with the exact origin of the trusted viewer.
4. Select **Add** and publish.

`frame-ancestors` lists the origins allowed to frame the page. Keep `'self'` if your own site should still be allowed. If the page is nested inside more than one frame, every ancestor must be allowed by the policy. Use a route pattern such as `/*` only when the same embedding rule should apply across the entire site.

Webstudio also sends `X-Frame-Options: SAMEORIGIN` by default. Modern browsers ignore `X-Frame-Options` when an enforced CSP includes `frame-ancestors`, so the explicit CSP rule allows the trusted origin. Older browsers that do not support `frame-ancestors` may still follow `X-Frame-Options` and block external framing. See the [CSP specification](https://www.w3.org/TR/CSP/#frame-ancestors-and-frame-options) and [MDN's `frame-ancestors` reference](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Content-Security-Policy/frame-ancestors).

## Publish and verify

Publish after adding or changing a rule. Then request the published page and inspect the response headers. For example:

```sh
curl -sS -D - -o /dev/null https://example.com/campaigns/spring
```

Check that the response includes the header and value you configured. Use a `GET` request as above; a `HEAD` request may not follow the same response path as a page load.

## Hosting behavior and limits

Webstudio Cloud applies configured headers to site responses, including redirects and site errors. Static assets served by the CDN keep their existing headers.

If you export and host the site elsewhere, configure response headers with your hosting provider. The Headers settings do not automatically configure that host.

You can add up to 100 rules. Header values must be non-empty, cannot contain line breaks or characters outside Latin-1, and can be up to 8 KB; all rules combined are limited to 16 KB. Cookies, connection-specific headers, and response-body framing headers cannot be configured.

## Related

* [Project settings](/university/foundations/project-settings)
* [Path patterns](/university/foundations/path-patterns)
* [Project settings: Authentication](/university/foundations/project-settings#authentication)
* [Publishing & custom domains](/university/foundations/publishing-and-custom-domains)


# Path patterns

Define page paths and target project-level Authentication and HTTP response header rules.

Webstudio uses path patterns to define page URLs and to target project-level **Authentication** and **HTTP response header** rules. These settings share named parameters, optional named parameters, and trailing wildcards. Page paths and project rules differ in which literal characters they accept.

Page settings and project-level rules validate and store paths separately, but use the same router for matching on published dynamic sites. Webstudio generates Remix routes from page paths and uses the underlying React Router matcher for Authentication and Headers rules. A static export has no app router; URL resolution depends on the hosting platform.

## Pattern syntax

Path patterns are slash-separated URL path segments. A segment can be a literal path name, a named parameter, an optional parameter, or a wildcard.

| Pattern         | Meaning                                           | Examples that match                            |
| --------------- | ------------------------------------------------- | ---------------------------------------------- |
| `/about`        | Exact static path                                 | `/about`                                       |
| `/blog/:slug`   | One required path segment named `slug`            | `/blog/hello-world`                            |
| `/blog/:slug?`  | One optional path segment named `slug`            | `/blog`, `/blog/hello-world`                   |
| `/docs/*`       | `/docs` and any number of path segments below it  | `/docs`, `/docs/setup`, `/docs/api/v1`         |
| `/files/:path*` | `/files` and any number of path segments below it | `/files`, `/files/image.png`, `/files/a/b.txt` |
| `/*`            | Any path on the site, including the home page     | `/`, `/about`, `/blog/post`                    |
| `/`             | The home page only                                | `/`                                            |

Use `:name` for a single segment, `:name?` for one optional segment, and `:name*` for a wildcard matching the remaining path. Parameter names use letters, numbers, and underscores. A parameter must occupy a complete segment; `*` and `:name*` must be the final segment. A literal segment cannot be optional: use `/:lang?/about` to make a segment optional, not `/en?/about`.

Non-root project rules and non-home page paths at the site root start with `/`. Neither accepts repeating slashes such as `//` or a trailing `/`. The project-rule pattern for the home page is `/`; [Page settings](/university/foundations/page-settings#path) stores the home page path as an empty value. A page inside a folder can also have an empty local path to use that folder's URL. Wildcards must be the final segment.

**Page settings** accepts lowercase letters and digits with the path syntax shown above. It rejects uppercase letters and percent-encoded paths.

**Project settings** accepts a wider range of literal characters. Use `?` only for an optional named parameter. URL-encoded `:`, `*`, and `?` are literal characters, not pattern syntax. For example, `/%2A` matches only that encoded pathname, with the same hex-letter case; it does not match every page.

## Matching behavior in Project settings

Project-level Authentication and Headers compare patterns against the URL pathname. The query string and fragment are not part of the match: `/docs?lang=en#start` is matched as `/docs`.

* Matching is case-insensitive: `/Docs` also matches `/docs`.
* A trailing slash is ignored for matching: `/about` matches both `/about` and `/about/`.
* A wildcard matches zero or more trailing segments. Therefore, `/docs/*` matches `/docs` as well as `/docs/setup`.
* A named wildcard works the same way for matching: `/files/:path*` matches `/files` and paths below it.
* An optional parameter matches either with or without that one segment: `/blog/:slug?` matches `/blog` and `/blog/post`, but not `/blog/post/comments`.
* A regular named parameter matches exactly one segment: `/blog/:slug` matches `/blog/post`, but not `/blog` or `/blog/post/comments`.
* A static path matches only that path: `/about` does not match `/about/team`.

When writing Authentication and Headers rules, use these examples to check the scope before publishing. A pattern such as `/*` is site-wide; `/` is only the home page.

## Page paths on the published site

Page Settings paths become routes in the generated Remix app. For example, Webstudio maps a dynamic segment such as `:slug` to a Remix dynamic route and a trailing `*` to a Remix splat route. Builder navigation and project rules now use the same router matching behavior. A rule for `/docs/*` matches `/docs` and paths below it in both places.

Page routes can provide parameter values to dynamic pages. Authentication and Headers use their patterns only to decide whether a rule applies; they do not expose route parameters. If several rules match, Authentication uses the first matching rule, while Headers applies each matching rule in order. For static exports, the hosting platform determines how page paths resolve.

## Choose a pattern

Start with the narrowest path that covers the pages you intend to affect:

* Use an exact path such as `/campaigns/spring` to target one page.
* Use a named parameter such as `/blog/:slug` when one segment can vary.
* Use an optional parameter such as `/blog/:slug?` when the segment may be absent.
* Use a wildcard such as `/docs/*` when a path and its descendants should share a rule.
* Use `/*` only when the rule should apply across the whole site.

For example, an Authentication rule for `/private/*` protects `/private` and paths beneath it. A Headers rule for `/campaigns/:slug` applies to each campaign page with one path segment after `/campaigns`.


# Modes

Modes change the Builder’s behavior, such as previewing your site without distractions.

There are three modes:

* [Design](#design)
* [Content](#content)
* [Preview](#preview)

You can change modes in the Top Bar or via [keyboard shortcuts](/university/foundations/shortcuts) if you have sufficient [permissions](/university/foundations/share-links#types-of-share-links).

Additionally, you can open any project in [Safe Mode](#safe-mode), which disables script execution for security and troubleshooting purposes.

<picture><source srcset="/files/i2NwYvqykMgOH0PWbTRQ" media="(prefers-color-scheme: dark)"><img src="https://330243581-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTep4EGj6hSC54ozHujyK%2Fuploads%2Fgit-blob-57f4391cbd164552833d61dd6f20d3c6a5bb709f%2Fbuilder-modes-light.png?alt=media" alt="Mode menu with Design and Content options and their keyboard shortcuts"></picture>

## Design

Design mode provides the full power of the Builder. For example, you can add and style components.

## Content

Content mode tailors the builder’s features to editor tasks like updating text and images, and adding templates to designer-specified regions. Text and supported props are editable only inside [Content Blocks](/university/core-components/content-block); everything outside them is read-only. This mode is ideal for team members and clients.

### Allowed actions

In Content mode, the following actions can be performed:

* Edit text content (e.g., paragraphs, headings, links, etc.) inside Content Blocks
* Add and edit links within text inside Content Blocks
* Upload and change images inside Content Blocks when their props are available in Content mode
* Insert instances of templates to designer-specified regions called [Content Blocks](/university/core-components/content-block)
* Edit MDX article fields and bodies inside connected Content Blocks, with changes saved back to the source file
* Create and edit entries in [content collection folders](/university/foundations/content-engine#make-the-folder-a-content-collection)
* Create pages from existing [Page templates](/university/foundations/page-templates), if the designer provided them
* Edit safe page settings: page name, static path, title, description, search visibility, language, social image, and custom metadata
* Publish the site (optional [permission](/university/foundations/share-links#types-of-share-links))

Content mode keeps structural and developer-oriented settings protected. Editors cannot create dynamic paths, change redirects, status codes, document type, authentication, page variables, or the page templates themselves.

### For designers/developers

As the website designer/developer, Content mode helps you strike the balance between creating a professional website while also letting your clients/team members edit it without them breaking it or deviating from the design system.

{% embed url="<https://youtu.be/hzRy45vIViY>" %}

Here’s what you need to know:

* You can try the Content mode by going to the Top Bar and changing modes.
* Users on the Pro tier can create a [Share link](/university/foundations/share-links) with “Content” permission
* Editors can perform [these actions](#allowed-actions)
* You can create templates that editors can insert with [Content Blocks](/university/core-components/content-block)
* You can connect MDX files to Content Blocks and configure collection folders so editors can create and edit complete articles visually
* You can create [Page templates](/university/foundations/page-templates) that editors can use to create new pages
* Editors can update safe page metadata, but dynamic routes, redirects, authentication, document type, and status-code logic remain under designer control

#### Creating a Content mode share link

1. Go to **Share** in the top bar
2. Create a new share link
3. Check "Content mode" option
4. Optionally enable/disable publish permission for the client

#### Testing Content mode

You can quickly switch to Content mode using the mode dropdown arrow next to Design in the top bar — useful for testing what clients will see before sharing the link.

#### Content Block templates

Content Block templates cannot be deleted by editors. Even if all instances of a template are removed, the template itself remains available for editors to add again. Make sure templates include complete styling since editors don't have access to the Style panel.

### For editors

With Content mode, you can dive right in and edit the website without feeling overwhelmed or risking any changes to the site that could cause issues.

{% embed url="<https://youtu.be/0GoOe5D-hQk>" %}

Here’s what you need to know:

* You can click directly on the canvas to update text inside a Content Block. Text outside a Content Block is read-only. Want to update a title to the latest promotion? Click, type, done.

  <figure><img src="https://330243581-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTep4EGj6hSC54ozHujyK%2Fuploads%2Fgit-blob-b6ed5a99d11eb9ca8e265660d7814d10d0005de7%2Fedit-text.gif?alt=media" alt="editing content on the canvas"><figcaption><p>Editing content on the canvas</p></figcaption></figure>
* Some items inside a Content Block may have settings, like images. Clicking on an image will display the settings on the right. There, you can upload a new image. Just be sure to fill in the field called “Alt” describing the image so visually impaired people can consume your site.

  <figure><img src="https://330243581-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTep4EGj6hSC54ozHujyK%2Fuploads%2Fgit-blob-ed2b3d7b8b1acfc2ea9ac89c81e68263a52d10ba%2Fimage-content-mode.png?alt=media" alt="image editing in content editor mode"><figcaption><p>The image can be replaced in the Settings Panel</p></figcaption></figure>
* The designer must include [Content Blocks](/university/core-components/content-block#content-block-in-content-mode) for content you need to change. These are the regions where you can edit content and add templates the designer provided. See [Content Block](/university/core-components/content-block) for more information.

  <figure><img src="https://330243581-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTep4EGj6hSC54ozHujyK%2Fuploads%2Fgit-blob-b6c262618bd1d9e22508a68af787d761a86453ae%2Fadd-content.png?alt=media" alt="Adding a template"><figcaption></figcaption></figure>
* In a [content collection](/university/foundations/content-engine#make-the-folder-a-content-collection), choose **New entry** to create an article with the form provided by the designer. Use **Entry settings** to edit its fields, **Edit file** to edit its source, or **Open on canvas** to edit the complete article on its configured dynamic page.
* If the designer provided [Page templates](/university/foundations/page-templates), you can create new pages from them and edit page details like name, path, SEO title, description, language, social image, and custom metadata.
* Changes save in real time, but you must publish them before they go live. Select **Publish** in the top bar, choose the destinations, and publish. **Publish** may be unavailable if the designer did not grant publishing permission.

## Preview

Preview mode hides editing capabilities so you can browse your website. Use it to test navigation, interactions, and responsive behavior on every breakpoint.

## Safe Mode

Safe mode is a security and troubleshooting feature that prevents all scripts from executing in your project. This is useful when:

* You suspect a script is causing issues or slowing down the builder
* You need to troubleshoot problems that might be caused by custom JavaScript
* You want to safely inspect a project before running potentially harmful code
* You're reviewing a marketplace template or imported project

### How to Enable Safe Mode

From the dashboard:

1. Right-click (or long press on mobile) on a project
2. Select **Open in safe mode** from the menu

When safe mode is active, you'll see a shield icon in the top bar indicating that script execution is disabled.

### What Safe Mode Disables

* All JavaScript in HTML Embed components
* Custom scripts added to the project
* Third-party scripts (analytics, widgets, etc.)

### What Still Works

* All styling and layout features
* Component rendering
* Text and image editing
* Building and publishing

{% hint style="info" %}
Safe mode only affects the builder preview. Published sites always run scripts normally, regardless of whether you edited the project in safe mode.
{% endhint %}

## Related

* [Share links](/university/foundations/share-links) – Create Content mode share links for clients
* [Content Block](/university/core-components/content-block) – Define editable regions for Content mode
* [Shortcuts](/university/foundations/shortcuts) – Keyboard shortcuts to switch modes quickly
* [Anatomy of the Webstudio builder](/university/foundations/anatomy-of-the-webstudio-builder) – Overview of the Builder interface


# Share links

Share links enable you to share your Webstudio Project with other people, set permissions, and create cloneable project copies.

**Use cases:**

* Providing one-off access to a site
* Cloning the Project to another Webstudio account
* Sharing your Project to get support
* [Selling templates](/contributing/marketplace#selling-templates) (i.e., using share links to deliver the template upon payment)
* Creating your own marketplace of templates

{% hint style="info" %}
For ongoing collaboration, invite people to a workspace from the [Dashboard](/university/foundations/dashboard#workspaces). For moving a project to another workspace or transferring it to another user, use the project menu in the Dashboard.
{% endhint %}

{% hint style="warning" %}
Share links should be treated like access keys. Anyone who gets the link can use it according to the permissions you set, so avoid sending share links through unsecured channels. Workspace membership invites are more secure for ongoing collaboration because access is tied to the recipient's Webstudio account and accepted from their Dashboard notifications.
{% endhint %}

## Cloning projects

When you share a project with **View** permission (or higher), recipients can clone the project to their own Webstudio account:

1. Create a Share link with at least **View** permission
2. Send the link to the recipient
3. They open the link and click the **Clone** button in the Builder
4. The project is copied to their account

{% hint style="info" %}
**Pro tip:** Use this feature to build a template marketplace. Create templates, share View links, and let users clone them into their accounts.
{% endhint %}

## Adding share links

Share links are created in the Top Bar in the Share dialog.

<figure><img src="https://330243581-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTep4EGj6hSC54ozHujyK%2Fuploads%2Fgit-blob-02b233bf1833f3be6aa57dad1fb28f0e2c5cfe98%2Fsharecontent.gif?alt=media" alt="creating a share link"><figcaption></figcaption></figure>

{% hint style="success" %}
There is no limit to how many share links you can add.
{% endhint %}

## Types of share links

| Type    | Is paid feature | Permissions                                                                                                                               |
| ------- | --------------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
| View    | No              | <ul><li>View</li><li>Copy instances (Pro users can disable copying)</li><li>Clone the Project (Pro users can disable cloning)</li></ul>   |
| Content | Yes             | <ul><li>Edit content only, such as text, images, and predefined components</li><li>Clone the Project</li><li>Publish (optional)</li></ul> |
| Build   | No              | <ul><li>Make any changes</li><li>Clone the Project</li><li>Can't publish</li></ul>                                                        |
| Admin   | Yes             | <ul><li>Make any changes</li><li>Clone the Project</li><li>Publish</li></ul>                                                              |

## Working with clients

When building a site for someone else, you may want to give them a cloneable copy, transfer the original project from the Dashboard, or provide limited editing access.

### Option 1: Share a cloneable copy

Create a new share link with cloning permissions (see [Types of share links](#types-of-share-links)). Then, send the link to your client so they can clone the Project into their Webstudio account.

{% hint style="warning" %}
If you are on a paid tier, it is tied to your account or workspace, not to the cloned project. The recipient needs a plan that covers any paid features or limits used by their copy.
{% endhint %}

If you plan to continue working on the website after they clone it, they can invite you to their workspace or create a Share link and send it to you.

### Option 2: Content mode

The Pro tier lets you create share links with the Content permission, allowing recipients to edit content inside [Content Blocks](/university/core-components/content-block) without designer-oriented features like the Style Panel. Content outside Content Blocks remains read-only.

To provide this, create a Share link, toggle “Content”, and send this link to your client.

See [Modes](/university/foundations/modes) to learn more about this.

## Related

* [Publishing & custom domains](/university/foundations/publishing-and-custom-domains) – Deploy your site and connect custom domains
* [Modes](/university/foundations/modes) – Switch between Design, Content, and Preview modes
* [Project settings](/university/foundations/project-settings) – Configure project-wide settings
* [Content Block](/university/core-components/content-block) – Create editable regions for Content mode users


# Publishing & custom domains

Learn how to connect a custom domain to your Project.

{% hint style="warning" %}
Many DNS providers do not allow adding a CNAME at the root/apex. If yours doesn’t, jump to [this section](#dns-provider-doesnt-allow-cname-flattening) for alternate options.
{% endhint %}

## Publish validation

Choose **Validate** in the publish dialog before publishing or exporting. Webstudio checks publishable pages for broken Resource references and invalid HTML nesting that could make the generated site fail or behave unexpectedly.

* **Errors** stop publishing until you resolve them.
* **Warnings** appear immediately but do not prevent publishing.

When a finding identifies a component, choose **Show element** to close the publish dialog, open the affected page, select the instance, and scroll it into view. Fix the problem, then publish again. Draft pages are excluded because they are not part of the generated site.

## Adding a custom domain

These steps will show you how to add a custom domain to your Project.

### 1. Add a new domain to your Project

Click "Publish" in the top bar, then “Add a new domain”.

Enter the domain or subdomain you want the site to be available at, for example, `www.example.com` or `example.com`.

After entering your domain, you will be provided with DNS records, which can be manually added to your DNS or automatically through Entri.

<figure><img src="https://330243581-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTep4EGj6hSC54ozHujyK%2Fuploads%2Fgit-blob-b04d00b92c730480f15e4accae5c19c2c136e9fc%2Fadd-new-domain.png?alt=media" alt="CNAME and TXT records for a new custom domain" width="362"><figcaption></figcaption></figure>

### 2. Add the DNS records

Next, you need to add the DNS records to your DNS provider either manually or automatically.

#### Option 1: Manually add DNS records

You can configure your custom domain manually by adding the provided `CNAME` and `TXT` records to your DNS.

1. **Open your DNS provider**
2. **Create a `CNAME` record**
   1. Add a new record.
   2. Set the type to `CNAME`.
   3. Copy the Name and Value from Webstudio.
   4. Paste them into the corresponding fields in your DNS provider. Note that the DNS provider might label the Value as "Target" or "Content."
3. **Create a `TXT` record**
   1. Add a new record.
   2. Set the type to `TXT`.
   3. Copy the Name and Value from Webstudio.
   4. Paste them into the corresponding fields in your DNS provider. Note that the DNS provider might label the Value as "Content".

#### Option 2: Automatically add DNS records with Entri

The Entri option makes configuring your domain extremely simple — no puzzling registrar UIs. You can do it without leaving Webstudio Builder in just a couple of clicks.

1. Click “Configure automatically”.
2. Click “Continue” on the Entri configurator. This process will analyze your root domain and detect your DNS settings.
3. Click on the Authorize button to redirect you to your DNS provider site. Log in, if required, and approve the configuration.
4. Return to Webstudio and complete the setup.

### 3. Verify and publish

Click "Check status" and once it's verified, republish your site.

{% hint style="info" %}
Verification may take up to 24 hours but usually takes only a few minutes.
{% endhint %}

{% hint style="warning" %}
You must publish your site *after* the domain is verified, or else "[Worker not found](#domain-issues)" will show on the site.
{% endhint %}

You can close the Publish dialog while the build runs. Reopen it to see the latest status. Resolve any reported error before trying again.

Each domain shows a yellow progress icon while publishing and a green checkmark once publishing finishes. A red icon indicates a failure. Hover over the icon to see the status and when publishing started.

Once your site is live, you can visit it by clicking the open icon next to the green checkmark.

***

## Publish to Staging

<picture><source srcset="/files/Go0IbTzhHYmnsYV8bznk" media="(prefers-color-scheme: dark)"><img src="https://330243581-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTep4EGj6hSC54ozHujyK%2Fuploads%2Fgit-blob-4d2228d16b885c0aac1c41202ef3ef6ef158407e%2Fstaging-domain-light.png?alt=media" alt="Publish dialog with a verified staging domain and Add a new domain, Export, and Publish actions"></picture>

You can publish your Project to a separate domain for testing before going live by only checking your staging domain, which can be the default Project subdomain or a custom domain.

{% hint style="info" %}
Every Project comes with a subdomain ending in "wstd.io". You can use this subdomain as your site’s staging environment. The domain is automatically no-indexed if you add a custom domain.
{% endhint %}

When publishing the site, optionally select the domain(s) you want to publish to. The workflow for testing/approval would be:

1. Make changes in the Builder
2. Open the publish dialog
3. Ensure only your subdomain is checked
4. Publish and share with your team/client
5. Upon approval, reopen the publish dialog and check your live/production domain.
6. Publish

### Staging Password Protection

All staging sites are protected by password by default. This protection cannot be removed — it's a platform security measure to prevent bad actors from using Webstudio staging domains for phishing attacks impersonating other brands, which is illegal and can result in Webstudio domains being blocked.

To share your staging site:

1. Open the publish dialog
2. Copy the staging link — it already contains the login credentials embedded in the URL
3. Share this link with your team or client — they'll be automatically authenticated without needing to enter credentials

{% hint style="info" %}
Click on the staging domain section in the publish dialog to view the username and password separately if needed.
{% endhint %}

For public access without password protection, publish to a custom domain instead.

***

## Standardizing on root or `www` using Cloudflare

{% hint style="info" %}
Why would you want to do this? Standardizing your domain through Cloudflare is free, flexible, and easy to manage, while also delivering excellent performance.
{% endhint %}

These instructions show you how to standardize your site’s primary domain in Cloudflare by choosing either `www` or the root domain.

{% hint style="info" %}
Note root domain is synonymous with apex, bare, and naked. An example is `example.com`.
{% endhint %}

1. Choose whether you want to use `www` or your root domain
2. Add your choice to Webstudio (e.g., `www.example.com` or `example.com`)
3. Add the provided records to your DNS
4. Redirect the domain you did *not* add to the domain you added by following the next sections.

### 1: Add a DNS record for the other domain

Cloudflare rules can’t apply to traffic that isn’t proxied through Cloudflare. Therefore, adding a DNS record for the domain you are *not* using is essential. Cloudflare offers an IP address for this exact use case.

**Create an A record and point it to `192.0.2.1`.**

> This address does not route traffic to an origin server but allows Cloudflare to apply rules, redirects, and Workers to incoming traffic. The equivalent IP address for an AAAA record is 100::.\
> \
> \- [Cloudflare](https://developers.cloudflare.com/fundamentals/setup/manage-domains/redirect-domain/)

Next, choose one of the following options based on your preferred setup.

### 2a: Redirect root to `www`

If you choose `www` as your primary domain, be sure to redirect the root domain to it. For example, `example.com` should redirect to `www.example.com`.

Follow [Cloudflare's guide](https://developers.cloudflare.com/rules/url-forwarding/examples/redirect-root-to-www/) to redirect your root domain to `www`.

### 2b: Redirect `www` to root

Although `www` is just a subdomain like `xyz.example.com`, many users still reference it out of habit. To ensure they reach your site, it’s best practice to redirect `www` to your root domain. To do this, make sure you’ve create the A record with `192.0.2.1` as described above, then follow [Cloudflare’s guide](https://developers.cloudflare.com/rules/url-forwarding/examples/redirect-www-to-root/) to set up a *Rule* in the Cloudflare dashboard.

***

## Exporting Projects

Webstudio can be self-hosted, putting you in control of your hosting, pricing, security, and compliance.

For more information about exporting and self-hosting, view [Self-Hosting](/university/self-hosting).

***

## Removing a domain

To remove a domain from Webstudio:

1. Click “Publish” in the top bar.
2. Click your domain.
3. Click “Remove domain”.

***

## Domain issues

Below are common issues when adding custom domains and how to resolve them.

### DNS provider doesn't allow CNAME flattening

While modern DNS providers like [Cloudflare](https://www.cloudflare.com/) support using CNAME at the apex, such as `example.com` (aka CNAME flattening), others only allow using CNAME with a subdomain, such as `www.example.com`.

#### Providers that don't support CNAME flattening

{% hint style="warning" %}
This list is *not* comprehensive.
{% endhint %}

* GoDaddy
* Hostinger
* Squarespace
* DigitalOcean
* Namecheap
* IONOS
* Hover

#### Option 1: Switch your DNS provider (without changing your domain registrar)

The easiest way to work around the CNAME limitation is to switch your DNS control over to a provider like [Cloudflare](https://developers.cloudflare.com/fundamentals/get-started/setup/add-site/). This process takes about 10 minutes and is free. Once you have migrated, you can use the original process detailed above to configure it.

{% hint style="info" %}
You can move the DNS (where the DNS records are managed) *without* needing to move the registration (i.e. where the domain was purchased), though it may make sense to move both.
{% endhint %}

**Why use Cloudflare**

Especially useful for agencies managing multiple client domains:

* Free plan is sufficient for most needs
* Additional CDN features and enhanced security
* Script injection capabilities (Facebook Pixel, Google Analytics)
* Faster DNS and better performance
* Easier management of multiple domains in one dashboard

**Steps to migrate to Cloudflare**

1. Sign up for [Cloudflare](https://www.cloudflare.com/) (free tier is sufficient)
2. Add your site/domain
3. Select the free plan
4. Cloudflare will scan and import existing DNS records automatically (email MX records, etc.)
5. Change nameservers at your registrar to the ones Cloudflare provides
6. Wait 15-20 minutes for propagation
7. Once verified, add Webstudio CNAME and TXT records in Cloudflare

{% hint style="info" %}
DNS propagation typically takes 10-15 minutes, but can take up to 72 hours in rare cases depending on registrar and location.
{% endhint %}

#### Option 2: Publish your website on a `www` subdomain

Go back to [Step 1](#1-add-a-new-domain-to-your-project), but this time, prefix your domain with `www`.

#### Video Tutorial

The above options are shown in the following video.

{% embed url="<https://www.youtube.com/watch?t=5s&v=4PaXK0e49ks>" %}

***

## Publishing to a Subdomain

Subdomains are useful for marketing teams who want autonomy over landing pages (e.g., `go.example.com`) while keeping the main site separate.

To publish to a subdomain:

1. Follow the same process as adding a custom domain
2. Use the subdomain (e.g., `go`) as the CNAME name instead of `@` or `www`
3. Add the corresponding TXT record

***

### Worker not found

You may see "Worker not Found" message when opening the site, like this:

<figure><img src="https://330243581-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTep4EGj6hSC54ozHujyK%2Fuploads%2Fgit-blob-73c8215804fcb52f3d02df205216e351bd579e66%2Fworker-not-found.png?alt=media" alt="Published site showing a Worker not found error"><figcaption></figcaption></figure>

Worker not found is due to one of the following reasons:

1. You haven't published your Project after adding a custom domain.
2. You clicked on publish, but it is still in the process of deployment. It usually takes 1 minute to distribute your Project across the globe. Webstudio is utilizing [Cloudflare's](https://workers.cloudflare.com/) advanced Edge network.
3. Your domain was not properly connected or you need to republish.

### **Adding a domain with a country code like `.co.uk`**

Using a second-level domain (SLD) within a country code top-level domain (ccTLD) such as `.co.uk` is fully supported. However, the DNS records provided when adding a domain in the Builder are incorrect.

Here are the provided records and correct records when adding `example.co.uk`:

| Record Type | Provided Value          | Correct Value   |
| ----------- | ----------------------- | --------------- |
| `CNAME`     | `example`               | `@`             |
| `TXT`       | `_webstudio_is.example` | `_webstudio_is` |

## Related

* [Cloudflare Website Builder](https://webstudio.is/cloudflare-website-builder) – Learn about Webstudio's Cloudflare integration
* [Project settings](/university/foundations/project-settings) – Configure redirects and site settings
* [Share links](/university/foundations/share-links) – Share projects and manage permissions
* [SEO settings](/university/foundations/seo-settings) – Optimize your site for search engines
* [Self-Hosting](/university/self-hosting) – Export and host your project independently


# Core Components

## Layout

{% content-ref url="/pages/Sk7hzOrDaMiFuKkkzo4I" %}
[Element](/university/core-components/element)
{% endcontent-ref %}

{% content-ref url="/pages/N9DKWseeQcjmwIK9xRYa" %}
[Slot](/university/core-components/slot)
{% endcontent-ref %}

{% content-ref url="/pages/lM2LMMBJ130cb38sUqSI" %}
[Separator](/university/core-components/separator)
{% endcontent-ref %}

## Typography

{% content-ref url="/pages/XFm8GHdex2OUxUA2lKnK" %}
[Text](/university/core-components/text)
{% endcontent-ref %}

{% content-ref url="/pages/wFg7U52qiJJVFSwun6ok" %}
[Heading](/university/core-components/heading)
{% endcontent-ref %}

{% content-ref url="/pages/OcJl23ZxHHhv5cDspnoH" %}
[Paragraph](/university/core-components/paragraph)
{% endcontent-ref %}

{% content-ref url="/pages/fe3CHNggzMoUS0jCH8k1" %}
[Inline Text](/university/core-components/inline-text)
{% endcontent-ref %}

{% content-ref url="/pages/nmE51aHGEzWPAY4dcNG5" %}
[Blockquote](/university/core-components/blockquote)
{% endcontent-ref %}

{% content-ref url="/pages/o5NMv3wcYYITlzpTVZhN" %}
[Code Text](/university/core-components/code-text)
{% endcontent-ref %}

## Navigation

{% content-ref url="/pages/TZ7hcLgqE8jU8bD6uZ6m" %}
[Link](/university/core-components/link)
{% endcontent-ref %}

{% content-ref url="/pages/Oz6BOSZJhZpx64TpG0Ml" %}
[Button](/university/core-components/button)
{% endcontent-ref %}

## Media

{% content-ref url="/pages/PhkOpX8lc9hlJ0GnCNQa" %}
[Image](/university/core-components/image)
{% endcontent-ref %}

{% content-ref url="/pages/cMoGj8hFEzE6k3zhLNGv" %}
[Video](/university/core-components/video)
{% endcontent-ref %}

{% content-ref url="/pages/dqNIXEVNYE2dOJos7WOS" %}
[Vimeo](/university/core-components/vimeo)
{% endcontent-ref %}

{% content-ref url="/pages/9igw0rr0C6wHbPBq0ThA" %}
[Vimeo Background Video](/university/core-components/vimeo-background-video)
{% endcontent-ref %}

{% content-ref url="/pages/0hvPWmXMoJEBwuZmOBAS" %}
[YouTube](/university/core-components/youtube)
{% endcontent-ref %}

## Forms

{% content-ref url="/pages/HwAtOOccOx1wYGY7bh7h" %}
[Form](/university/core-components/form)
{% endcontent-ref %}

{% content-ref url="/pages/BOLzVr7aUHMk0zUSkMl7" %}
[Webhook Form (deprecated)](/university/core-components/webhook-form)
{% endcontent-ref %}

{% content-ref url="/pages/06fXxOPXdChiFDydmTZo" %}
[Input](/university/core-components/input)
{% endcontent-ref %}

{% content-ref url="/pages/JgU3Dgc7kIXClZ6W4sKN" %}
[Text Area](/university/core-components/textarea)
{% endcontent-ref %}

{% content-ref url="/pages/WzhGVdLng4qXTu9snXnX" %}
[Label](/university/core-components/label)
{% endcontent-ref %}

{% content-ref url="/pages/6xmgZiWICewvgFF1iuSu" %}
[Checkbox](/university/core-components/checkbox)
{% endcontent-ref %}

{% content-ref url="/pages/4A3Vx4JveHaTeVS26BFp" %}
[Radio Button](/university/core-components/radio-button)
{% endcontent-ref %}

{% content-ref url="/pages/LGKAegoKjDzSgR3mpzek" %}
[Select](/university/core-components/select)
{% endcontent-ref %}

## Data

{% content-ref url="/pages/cpsvtS3mPDmYsoEOnQ6Z" %}
[List](/university/core-components/list)
{% endcontent-ref %}

{% content-ref url="/pages/upvKD5ydTvwgUhafJuAW" %}
[Collection](/university/core-components/collection)
{% endcontent-ref %}

{% content-ref url="/pages/2pY5IGDefvOgBTofmVWY" %}
[Content Embed](/university/core-components/content-embed)
{% endcontent-ref %}

{% content-ref url="/pages/U0d7kLBu3b0nNI9hCxKk" %}
[Content Block](/university/core-components/content-block)
{% endcontent-ref %}

## Embeds

{% content-ref url="/pages/rHKKkqfgRmjo89BY2EvK" %}
[HTML Embed](/university/core-components/html-embed)
{% endcontent-ref %}

{% content-ref url="/pages/kuK1zd7s0VXAsx4PszQD" %}
[Markdown Embed](/university/core-components/markdown-embed)
{% endcontent-ref %}

## Animation

{% content-ref url="/pages/Yfv03kfWfUlmBnoYqXVf" %}
[Animation Group](/university/core-components/animation-group)
{% endcontent-ref %}

{% content-ref url="/pages/ISNW9VxhCN1YXwcidiCV" %}
[Text Animation](/university/core-components/text-animation)
{% endcontent-ref %}

{% content-ref url="/pages/WYUnslrPc46HYwpJtJ9O" %}
[Video Animation](/university/core-components/video-animation)
{% endcontent-ref %}

{% content-ref url="/pages/ZY6DAFsn3VqTb2FMcovV" %}
[Stagger Animation](/university/core-components/stagger-animation)
{% endcontent-ref %}

## Head / SEO

{% content-ref url="/pages/Xtn1EdVs7W75EqJFrsZf" %}
[Head Slot](/university/core-components/head-slot)
{% endcontent-ref %}

{% content-ref url="/pages/Zp9VA5IoAAeLIN31AOWw" %}
[JSON-LD](/university/core-components/json-ld)
{% endcontent-ref %}

## XML / Localization

{% content-ref url="/pages/A1to1BfBfppRrPS83ZHe" %}
[XML Node](/university/core-components/xml-node)
{% endcontent-ref %}

{% content-ref url="/pages/0qe4pwKjR4vSyNucg4pr" %}
[Time](/university/core-components/time)
{% endcontent-ref %}


# Element

The Element component is a flexible container for building layouts in Webstudio.

The Element component is a versatile component that can represent any HTML element. It provides the flexibility to create semantic HTML structures with any tag type while maintaining full styling capabilities.

***

## How to Use the Element Component

The Element component can be found in **Components > General**, and you can place it on your canvas by dragging and dropping it or clicking it in the Components panel.

<picture><source srcset="/files/GW27fUw54Kz1uYwqQkZZ" media="(prefers-color-scheme: dark)"><img src="https://330243581-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTep4EGj6hSC54ozHujyK%2Fuploads%2Fgit-blob-d83bd1cac27b29a5fd037b3f8d6a27521380d365%2Fcomponents-panel-light.png?alt=media" alt="Components panel with Element listed first in the expanded General group"></picture>

***

## Available Tags

The Element component can be rendered as many HTML tags, including but not limited to:

### Container Elements

* `div` (default) – Generic container
* `section` – Document section
* `article` – Self-contained content
* `aside` – Sidebar content
* `header` – Header section
* `footer` – Footer section
* `main` – Main content
* `nav` – Navigation section

### Text Elements

* `span` – Inline container
* `p` – Paragraph (rendered as block)

### List Elements

* `ul` – Unordered list
* `ol` – Ordered list
* `li` – List item

### Link Element

* `a` – Anchor/link (shows href and target properties)

### Table Elements

* `table` – Table container
* `thead` – Table header group
* `tbody` – Table body group
* `tfoot` – Table footer group
* `tr` – Table row
* `th` – Table header cell
* `td` – Table data cell

### Other Elements

* `address` – Contact information
* `figure` – Figure with optional caption
* `figcaption` – Figure caption
* `label` – Form label
* `dl` – Description list
* `dt` – Description term
* `dd` – Description details

***

## Changing the Tag

You can change the HTML tag of the Element by opening the **Settings Panel** located on the right side of the Builder.

<picture><source srcset="/files/W75zrVDTMhul9CYqiaek" media="(prefers-color-scheme: dark)"><img src="https://330243581-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTep4EGj6hSC54ozHujyK%2Fuploads%2Fgit-blob-512f4ea536b9b2e9f148cb15e386efd83851cfba%2Felement-settings-panel-light.png?alt=media" alt="Settings panel for a paragraph element with Name, Show, Tag, ID, and Class controls"></picture>

The tag dropdown shows all available HTML elements you can use. When you change the tag, the component's properties automatically update to show relevant attributes for that element type.

***

## Tag Labels in Navigator

When viewing Elements in the Navigator, the component label displays the HTML tag name (e.g., "section", "nav", "article"). This makes it easy to identify the semantic structure of your page at a glance.

{% hint style="info" %}
You can rename Elements in the Navigator by double-clicking to give them more descriptive names like "Hero Section" or "Main Navigation" while still maintaining the correct HTML tag.
{% endhint %}

***

## Use Cases

### Semantic HTML Structure

Use Element to create proper HTML5 semantic markup:

* `header` for site headers
* `nav` for navigation menus
* `main` for primary content
* `article` for blog posts
* `aside` for sidebars
* `footer` for footers

### Custom Tables

Build accessible data tables using Element with table-related tags (`table`, `thead`, `tbody`, `tr`, `th`, `td`).

### Description Lists

Create glossaries or key-value displays with `dl`, `dt`, and `dd` tags.

### Links with Full Control

Use the `a` tag to create links with full styling control and access to the href and target properties.

***

## Element vs Box

The Element component provides more flexibility than the Box component:

| Feature         | Element            | Box              |
| --------------- | ------------------ | ---------------- |
| Tag options     | Any HTML tag       | div only         |
| Semantic HTML   | ✅ Full support     | ❌ Limited        |
| Navigator label | Shows HTML tag     | Shows "Box"      |
| Use case        | Semantic structure | Quick containers |

{% hint style="success" %}
For building semantic, accessible websites, prefer Element over Box when you need specific HTML tags.
{% endhint %}

## Related

* [Slot](/university/core-components/slot) – Create reusable component slots
* [Link](/university/core-components/link) – Navigation and anchor elements
* [HTML Embed](/university/core-components/html-embed) – Custom HTML code


# Link

Link lets you link to websites, pages, emails, and more from text, images, and more.

> See [MDN: \<a>](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a)

{% embed url="<https://www.youtube.com/watch?v=XPzFZ67zRrU>" %}

***

## How to use the Link component

The "Link Component" can be found in Components > General, and you can place it on your canvas by dragging and dropping it or clicking it in the Components panel.

You can convert anything into a link by wrapping it inside the Link component, including text and images. These links can direct users to different pages within your site or lead to external resources, including other websites, downloadable files, or email addresses.

***

## How to customize a Link instance's properties

You can customize the properties of a Link instance by selecting it and going to "Settings." Here is an overview of each property:

### Href

<figure><img src="https://330243581-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTep4EGj6hSC54ozHujyK%2Fuploads%2Fgit-blob-7cfcd675d9077d63c14dddc0a11e5989b860b44a%2Flink-href-property.avif?alt=media" alt="Link settings with the Href property available for a destination"><figcaption></figcaption></figure>

The "href" property determines what the Link instance will lead to, such as a URL, page, phone, attachment, or email address.

1. **URL**: In its most common form, the "href" property references a URL, linking to another website or page.
2. **Page**: You can link to all of your pages within your site here. You can also link to specific sections within those pages. To link to a section on a page, first go to the section and fill out the `ID` field in Settings. Then go back to the link, select the page, and then in the next dropdown select the `ID` you just created. Like this:

   <div align="center"><figure><img src="https://330243581-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTep4EGj6hSC54ozHujyK%2Fuploads%2Fgit-blob-6f79fb5adcff83ec33bd0816a0f7c81c0a1ff89e%2Fsection-id.png?alt=media" alt="Section settings with an ID entered as the anchor destination" width="375"><figcaption><p>First add the ID to the section you want to link to</p></figcaption></figure></div>

   <div align="center"><figure><img src="https://330243581-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTep4EGj6hSC54ozHujyK%2Fuploads%2Fgit-blob-7efc28b96c91994e64de3f9760ea92e76f1e286a%2Flink-to-section.png?alt=media" alt="Link destination picker with a section selected on the current page" width="375"><figcaption><p>Then add a page link and select the section.</p></figcaption></figure></div>
3. **Email**: When you specify an email address as the 'href' value, the link opens the user's default email client (such as Gmail) with the designated email address pre-filled.
4. **Phone**: If you set the "href" property to a phone number, the link becomes a prompt for users to initiate phone calls directly from their devices.
5. **Attachment**: You can also link to downloadable attachments such as PDFs, documents, or media files, allowing users to initiate file downloads with one click.

### Target

The `target` property controls where the link opens. Choose one of these values:

1. **`_self`**: Opens the link in the same tab. This is the default.
2. **`_blank`**: Opens the link in a new tab or window.
3. **`_parent`**: Opens the link in the parent browsing context. This is mainly useful inside nested frames.
4. **`_top`**: Opens the link in the top-level browsing context, replacing any frames.

To open a link in a new tab, set **Target** to **`_blank`**.

### Prefetch

The "Prefetch" property enables near-instant page transitions by preloading linked pages before the user clicks. This dramatically improves perceived navigation speed.

1. **Intent**: The browser loads the destination page when the user hovers over the link. This is ideal for most links as it balances performance with resource usage.
2. **Render**: The browser loads all destination pages as soon as the current page renders. Best for simple pages or funnels with very few links.
3. **Viewport**: The browser loads the destination page when the link enters the user's viewport. Good for links that appear below the fold.

For most websites, use "Intent" or "Viewport" to provide fast navigation without overloading the browser with pages to preload.

## Wrapping Components in Links

You can add any Webstudio component inside a Link element to make it clickable — images, videos, text, buttons, or even custom HTML embeds. Simply drag the component into the Link or wrap existing content by selecting it and using the Link component.

## How to style the current page state

When using links for navigation and wanting to highlight the current page, the link component has a "Local Link" state in every style source.

<figure><img src="https://330243581-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTep4EGj6hSC54ozHujyK%2Fuploads%2Fgit-blob-d1d907b18d8a055faa7ff7a4fdd6014aba047b64%2Flink-local-link-state.png?alt=media" alt="Style sources showing the current-page state for a local link"><figcaption></figcaption></figure>

## Related

* [Button](/university/core-components/button) – Clickable action buttons
* [Element](/university/core-components/element) – Generic HTML elements
* [Navigation Menu](/university/radix/navigation-menu) – Navigation with dropdowns


# Slot

Slots are containers for anything that you want to re-use across your site, like a nav menu.

{% embed url="<https://vimeo.com/842097297>" %}

{% embed url="<https://www.youtube.com/watch?v=MWTpgOmv6N0>" %}

Slots enable you to create reusable sections on your site. Changes made to one instance of the Slot will automatically change all other instances.

{% hint style="info" %}
Slots and their children will appear purple in the navigator and canvas to indicate they are Slots, and any changes made to them will affect all other instances of the Slot.
{% endhint %}

Slots are most commonly used to create reusable navigations and footers, though they are helpful for any sections you want to reuse throughout your site.

{% hint style="info" %}
Data variables defined on the Global Root are accessible within Slots, while those defined on other instances outside the Slot are not.
{% endhint %}

***

## How to use Slot component

Here is the process for creating and reusing Slot instances:

### Creating a Slot instance

![Slot component and its source content on the canvas](https://330243581-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTep4EGj6hSC54ozHujyK%2Fuploads%2Fgit-blob-b18897c33d415ad3dc6b4049837259aa583e444a%2Fslot-component-overview.png?alt=media\&token=97c90420-4330-4f3d-838c-b7a8646f81c2)

1. You can find the Slots component in the “Components Panel.”
2. Drag “Slot” from the Components Panel onto your canvas to create a Slot instance. Alternatively, you can add it to the currently selected instance with a click.
3. Add other instances from the Components Panel into the Slot instance to populate it.

### Reusing a Slot instance

Once you have a Slot on the canvas you can re-use it by simply copying and pasting it anywhere in your project.

1. Select your Slot instance and press Ctrl + C (for Windows) or CMD + C (for Mac).
2. Now, select the position where you want to insert the Slot instance and press Ctrl + V (for Windows) or CMD + V (for Mac)

And that’s it! Now updates that you make to any Slot will update all other instances of that Slot.

If you need a new unique Slot instance, simply add a new Slot from the Add panel and it will not interfere with your existing Slots.

## Create shared Slots with AI agents

Webstudio MCP can convert an existing section into a shared Slot and insert linked copies elsewhere in the Project. The agent can also add another copy of an existing Slot to a different page. Every copy points to the same shared content, so later edits remain synchronized in the Builder.

Ask the agent to inspect the target parent before inserting a Slot. Webstudio validates component nesting, prevents a Slot from being inserted inside its own content, and rejects ambiguous instance paths instead of guessing which shared occurrence to change.

***

## Conclusion

With Slots you can re-use content across your site and sync the changes you make to them.

## Related

* [Content Block](/university/core-components/content-block) – Editable content areas
* [Element](/university/core-components/element) – HTML element containers
* [Collection](/university/core-components/collection) – Dynamic content iteration


# Separator

Add visual dividers between content sections in Webstudio.

> See [MDN: \<hr>](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/hr)

The Separator component creates a visual divider between content sections. It renders as a semantic `<hr>` (horizontal rule) element or can be styled as a vertical divider.

## When to Use

Use Separator for:

* Dividing sections of content
* Separating items in a list or menu
* Creating visual breaks in long content
* Organizing navigation or sidebar elements

## How to Use

1. Drag a **Separator** component from Components > General onto your canvas
2. Position it between the content sections you want to divide
3. Style using the Style Panel (color, thickness, margins)

## Styling

### Horizontal Separator (Default)

```css
width: 100%;
height: 1px;
background-color: #e0e0e0;
border: none;
margin: 16px 0;
```

### Vertical Separator

For a vertical divider (useful in horizontal layouts):

```css
width: 1px;
height: 100%;
background-color: #e0e0e0;
align-self: stretch;
margin: 0 16px;
```

### Decorative Separator

Create more decorative dividers:

```css
width: 50%;
height: 2px;
background: linear-gradient(to right, transparent, #primary, transparent);
margin: 32px auto;
```

## Properties

Some commonly used properties (see the Settings panel for all available options):

| Property        | Description                              |
| --------------- | ---------------------------------------- |
| **orientation** | `horizontal` (default) or `vertical`     |
| **decorative**  | If true, removes from accessibility tree |

## Related

* [Element](/university/core-components/element) – Generic HTML containers
* [List](/university/core-components/list) – Ordered and unordered lists
* [Blockquote](/university/core-components/blockquote) – Quoted content


# Text

Add and style text content in Webstudio with the Text component.

> See [MDN: \<span>](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/span)

The **Text** component is used to display text content in your Webstudio projects. It provides a flexible container for adding and styling text.

## Overview

Text is the primary component for adding readable content to your pages. Unlike the Heading or Paragraph components which have more specific semantic purposes, Text is a general-purpose text container.

## Tag Options

Text can render different HTML elements:

| Tag          | Use Case                               |
| ------------ | -------------------------------------- |
| `div`        | Block-level text container (default)   |
| `span`       | Inline text container                  |
| `cite`       | Citation or reference to creative work |
| `figcaption` | Caption for a figure element           |

## Properties

| Property | Type   | Description                             |
| -------- | ------ | --------------------------------------- |
| `tag`    | string | HTML element to render (default: `div`) |
| `id`     | string | Unique identifier for the element       |
| `class`  | string | CSS class names                         |

## When to Use Text vs Other Components

| Component     | Use Case                               |
| ------------- | -------------------------------------- |
| **Text**      | General text content, captions, labels |
| **Heading**   | Section titles (h1-h6)                 |
| **Paragraph** | Body text, article content             |
| **Span**      | Inline text styling within other text  |

## Styling Text

### Typography Properties

* **Font Family**: Choose from system fonts or custom fonts
* **Font Size**: Set the size in various units (px, rem, em)
* **Font Weight**: Light, normal, medium, bold, etc.
* **Line Height**: Spacing between lines
* **Letter Spacing**: Spacing between characters
* **Text Align**: Left, center, right, justify
* **Color**: Text color

### Text Effects

* **Text Shadow**: Add shadow effects
* **Text Transform**: Uppercase, lowercase, capitalize
* **Text Decoration**: Underline, overline, line-through
* **Text Overflow**: Ellipsis for truncated text

## Rich Text Editing

When you double-click on a Text component in the canvas, you enter rich text editing mode where you can:

* Apply **Bold** formatting
* Apply *Italic* formatting
* Add [Links](/university/core-components/link)
* Use inline formatting like Superscript and Subscript

## Dynamic Content

Text components can display dynamic content using variables:

1. Select the Text component
2. In the Settings panel, click the binding icon next to content
3. Choose a variable or expression

Example use cases:

* Display CMS content
* Show user data
* Render computed values

## Best Practices

1. **Use semantic components** - Choose Heading for titles and Paragraph for body text when appropriate
2. **Set readable font sizes** - Body text should typically be 16px or larger
3. **Maintain contrast** - Ensure sufficient color contrast between text and background
4. **Use appropriate line height** - 1.4-1.6 for body text improves readability
5. **Limit line length** - Keep lines to 50-75 characters for optimal readability

## Related Components

* [Heading](/university/core-components/heading) - For section titles
* [Paragraph](/university/core-components/paragraph) - For body text
* [Inline Text](/university/core-components/inline-text) - For inline text styling
* [Link](/university/core-components/link) - For clickable text


# Heading

Add headings (H1-H6) to structure content in Webstudio.

> See [MDN: Heading elements](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/Heading_Elements)

The **Heading** component renders HTML heading elements (h1-h6) for creating a hierarchical structure of titles and subtitles on your page.

## Overview

Headings establish the document outline and help users and search engines understand your content structure. They're crucial for:

* **Accessibility**: Screen readers use headings for navigation
* **SEO**: Search engines use headings to understand page content
* **Visual hierarchy**: Guide users through your content

## Tag Options

| Tag  | Typical Use                         |
| ---- | ----------------------------------- |
| `h1` | Page title (use only once per page) |
| `h2` | Major section titles                |
| `h3` | Subsection titles                   |
| `h4` | Sub-subsection titles               |
| `h5` | Minor headings                      |
| `h6` | Smallest headings                   |

## Properties

| Property | Type   | Description                                 |
| -------- | ------ | ------------------------------------------- |
| `tag`    | string | Heading level: h1, h2, h3, h4, h5, or h6    |
| `id`     | string | Unique identifier (useful for anchor links) |
| `class`  | string | CSS class names                             |

## Heading Hierarchy

Always maintain proper heading hierarchy:

```
h1: Page Title
├── h2: Section 1
│   ├── h3: Subsection 1.1
│   └── h3: Subsection 1.2
├── h2: Section 2
│   ├── h3: Subsection 2.1
│   │   └── h4: Sub-subsection 2.1.1
│   └── h3: Subsection 2.2
└── h2: Section 3
```

### Common Mistakes to Avoid

❌ **Don't skip levels**: Going from h2 directly to h4\
❌ **Don't choose heading level for style**: Use CSS instead\
❌ **Don't use multiple h1 tags**: One per page is the standard

## Styling Headings

### Default Preset Styles

Webstudio applies browser-normalized styles to headings. You can customize:

* **Font Size**: Each heading level typically has progressively smaller sizes
* **Font Weight**: Often bold (700) or semi-bold (600)
* **Line Height**: Tighter than body text (1.1-1.3)
* **Margin**: Space above and below headings
* **Color**: Can match or contrast with body text

### Typography Scale

A common typographic scale for headings:

| Level | Size (desktop) | Size (mobile) |
| ----- | -------------- | ------------- |
| h1    | 48-64px        | 32-40px       |
| h2    | 36-48px        | 28-32px       |
| h3    | 28-32px        | 24-28px       |
| h4    | 24-28px        | 20-24px       |
| h5    | 20-24px        | 18-20px       |
| h6    | 16-18px        | 16-18px       |

## Dynamic Headings

Headings can display dynamic content from:

* CMS collections
* Page variables
* URL parameters

To bind dynamic content:

1. Select the Heading
2. Click the binding icon in Settings
3. Choose your data source

## Anchor Links

Create anchor links to headings for in-page navigation:

1. Set an `id` on the Heading (e.g., "features")
2. Create a [Link](/university/core-components/link) with href `#features`
3. Clicking the link scrolls to that heading

## SEO Best Practices

1. **Include keywords**: Place important keywords in headings naturally
2. **Be descriptive**: Headings should clearly describe the content that follows
3. **Keep h1 unique**: Each page should have exactly one h1 that describes the page's main topic
4. **Front-load important words**: Put key terms at the beginning of headings

## Related Components

* [Text](/university/core-components/text) - General text content
* [Paragraph](/university/core-components/paragraph) - Body text


# Paragraph

Add paragraph text blocks to your Webstudio pages.

> See [MDN: \<p>](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/p)

The **Paragraph** component renders an HTML `<p>` element, used for blocks of body text content.

## Overview

Paragraphs are the primary component for body text and article content. They automatically include proper spacing and are semantically correct for running text.

## Properties

| Property | Type   | Description       |
| -------- | ------ | ----------------- |
| `id`     | string | Unique identifier |
| `class`  | string | CSS class names   |

## When to Use Paragraph

Use Paragraph for:

* Article body content
* Descriptions and explanations
* Any block of running text

Use other components for:

* Titles → [Heading](/university/core-components/heading)
* Labels or short text → [Text](/university/core-components/text)
* Inline styled text → [Inline Text](/university/core-components/inline-text)

## Styling Paragraphs

### Typography

* **Font Size**: 16px minimum recommended for readability
* **Line Height**: 1.5-1.7 for comfortable reading
* **Max Width**: 65-75 characters per line is optimal
* **Margin**: Add spacing between paragraphs

### Readable Text Tips

1. **Sufficient contrast**: Minimum 4.5:1 ratio with background
2. **Comfortable line length**: Use max-width to limit text width
3. **Adequate spacing**: Line height of at least 1.5
4. **Clear font**: Use readable typefaces at appropriate sizes

## Rich Text Formatting

When editing a Paragraph, you can apply inline formatting:

* **Bold** text for emphasis
* *Italic* text for titles or foreign words
* [Links](/university/core-components/link) for navigation
* <sup>Superscript</sup> for footnotes
* <sub>Subscript</sub> for chemical formulas

## Dynamic Content

Paragraphs work well with dynamic content:

```
Collection
└── Paragraph (bound to post.content)
```

For long-form CMS content, consider using:

* [Content Embed](/university/core-components/content-embed) for rich text
* [Markdown Embed](/university/core-components/markdown-embed) for markdown

## Best Practices

1. **One idea per paragraph**: Keep paragraphs focused
2. **Use semantic markup**: Paragraph for text, Heading for titles
3. **Maintain readability**: Proper sizing, spacing, and contrast
4. **Consider mobile**: Text should be readable on all devices

## Related Components

* [Text](/university/core-components/text) - General text container
* [Heading](/university/core-components/heading) - Section titles
* [Content Embed](/university/core-components/content-embed) - Rich text from CMS


# Inline Text

Style inline text spans within paragraphs in Webstudio.

> See [MDN: \<span>](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/span)

Webstudio provides several components for inline text formatting within paragraphs and other text containers.

## Overview

Inline formatting components allow you to style specific portions of text without affecting the entire text block. These components are typically used within [Paragraph](/university/core-components/paragraph), [Text](/university/core-components/text), or [Heading](/university/core-components/heading) components.

## Components

### Span

The `<span>` element is a generic inline container for styling text.

**Use cases:**

* Apply custom colors to specific words
* Add backgrounds to highlighted text
* Apply multiple styles to a text portion

**Properties:**

| Property | Type   | Description       |
| -------- | ------ | ----------------- |
| `id`     | string | Unique identifier |
| `class`  | string | CSS class names   |

### Bold

The `<strong>` element indicates text with strong importance.

**Use cases:**

* Important warnings or key terms
* Emphasize critical information
* Highlight product names or features

**Properties:**

| Property | Type   | Description       |
| -------- | ------ | ----------------- |
| `id`     | string | Unique identifier |
| `class`  | string | CSS class names   |

> **Note**: Bold has semantic meaning for screen readers. Use it for important content, not just visual styling.

### Italic

The `<em>` element indicates emphasized text.

**Use cases:**

* Technical terms on first use
* Titles of works (books, movies)
* Foreign words or phrases
* Emphasis in speech

**Properties:**

| Property | Type   | Description       |
| -------- | ------ | ----------------- |
| `id`     | string | Unique identifier |
| `class`  | string | CSS class names   |

### Superscript

The `<sup>` element displays text slightly above the normal line.

**Use cases:**

* Footnote references¹
* Mathematical exponents (x²)
* Trademark symbols (™)
* Ordinal indicators (1st, 2nd)

**Properties:**

| Property | Type   | Description       |
| -------- | ------ | ----------------- |
| `id`     | string | Unique identifier |
| `class`  | string | CSS class names   |

### Subscript

The `<sub>` element displays text slightly below the normal line.

**Use cases:**

* Chemical formulas (H₂O)
* Mathematical subscripts
* Footnotes (in some styles)

**Properties:**

| Property | Type   | Description       |
| -------- | ------ | ----------------- |
| `id`     | string | Unique identifier |
| `class`  | string | CSS class names   |

## Using Rich Text Editing

The easiest way to apply inline formatting is through rich text editing:

1. Double-click on a text component to enter edit mode
2. Select the text you want to format
3. Use the formatting toolbar:
   * **B** for Bold
   * *I* for Italic
   * Link icon for hyperlinks

For Superscript and Subscript, you'll need to add the component manually and position it within your text.

## Styling Tips

### Custom Highlights

```
Paragraph
└── Span (background: yellow, padding: 2px 4px)
    └── "highlighted text"
```

### Gradient Text

Apply a gradient to text using Span:

1. Add Span around text
2. Set background to gradient
3. Add `-webkit-background-clip: text`
4. Set color to transparent

### Multiple Styles

Nest formatting components for combined effects:

```
Paragraph
└── Bold
    └── Italic
        └── "bold and italic text"
```

## Accessibility Considerations

1. **Use semantic elements**: Bold (`<strong>`) conveys importance to screen readers
2. **Don't rely on color alone**: Add additional indicators for important information
3. **Maintain contrast**: Styled text should still be readable
4. **Use sparingly**: Too much formatting reduces readability

## Related Components

* [Text](/university/core-components/text) - General text container
* [Paragraph](/university/core-components/paragraph) - Block text content
* [Link](/university/core-components/link) - Clickable text


# Blockquote

Display quotations with the Blockquote component in Webstudio.

> See [MDN: \<blockquote>](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/blockquote)

{% embed url="<https://www.youtube.com/watch?v=reVman0DMWM>" %}

The Blockquote Component is used to highlight quoted content on a webpage, enhancing its impact and readability. You can use it in your Webstudio project to emphasize text that has been taken directly from another source, such as quotes, excerpts, or references.

***

## How to use the Blockquote component

The "Blockquote" component can be found in Components > Text, and you can place it on your canvas by dragging and dropping it or clicking it in the Components panel. To edit the content of your Blockquote instance, simply double-click it.

Once the component is placed on your canvas, you can customize its appearance using the Style panel on the right. You can also create a design token for this styling. This allows you to apply consistent styling on multiple blockquotes across your project by reusing the same design token for each instance.

For more on Design tokens in Webstudio, refer to [this guide](/university/foundations/design-tokens).

## Styling a Blockquote

By default, Blockquote components include padding on the left and right, along with a left border. You can customize these in the Style panel:

* **Border**: Adjust the border width and color under Border settings to match your brand
* **Padding**: Modify the default padding to adjust spacing around your quote text

When using a Cite tag inside your blockquote, you may want to change its display property from "inline" to "block" to position the citation on its own line below the quote.

***

## How to attribute a Blockquote

It is a good practice to attribute the quoted text you use in a blockquote with the source's title, author, publication, URL, or any relevant information that identifies where the quote came from. This maintains transparency and credibility in your content. It also allows readers to verify the accuracy of the information and fosters ethical use of others' words.

You can add an attribution or citation by using the “Cite” property in your Blockquote instance settings or with the “Cite” Tag from a Text component.

### Using the Cite property

<figure><img src="https://330243581-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTep4EGj6hSC54ozHujyK%2Fuploads%2Fgit-blob-95caa2f647b6d69da8f065ad586d83b76c1fbd83%2Fblockquote-cite-property.avif?alt=media" alt="Blockquote Cite property in the Settings panel"><figcaption></figcaption></figure>

If you want to include a URL link as a citation:

1. Choose your blockquote instance in the canvas or the Navigator.
2. Go to Settings > Properties on the right.
3. Add the URL in the "Cite" property.

Please note that this value will not be visible to the end-user.

### Using the Cite tag

<figure><img src="https://330243581-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTep4EGj6hSC54ozHujyK%2Fuploads%2Fgit-blob-65bdc40b6318c874231fc91435ad873d6c5c95c2%2Fblockquote-cite-tag.avif?alt=media" alt="Cite tag selected for text inside a Blockquote"><figcaption></figcaption></figure>

If you want to include a Name/Title as a citation alongside your Blockquote:

1. Add a Text component to your Blockquote instance by dragging and dropping it or clicking it in the Components panel.
2. After positioning it correctly, go to Settings and select Tag > Cite.
3. Double-click the "Cite" instance on your canvas to edit it and add your citation.

Please note that this value will appear as italicized text to the end-user.

## Related

* [Paragraph](/university/core-components/paragraph) – Standard text paragraphs
* [Text](/university/core-components/text) – Inline text styling
* [Heading](/university/core-components/heading) – Section headings


# Alert

Highlight notes, tips, warnings, and other important information with the Alert component.

Use Alert for information that deserves extra attention without interrupting the main content.

## How to use Alert

Add Alert from **Components > Text**, then edit its paragraph or add other content inside it.

Choose **Variant** under **Properties & attributes** to set the alert to **Note**, **Tip**, **Important**, **Warning**, or **Caution**.

## Styling

Style the Alert instance for its container appearance. The selected variant produces the corresponding component state, so you can style **Note**, **Tip**, **Important**, **Warning**, and **Caution** independently in the states menu.

Alerts created from GitHub-style syntax in a Content Block `.mdx` file become Alert component instances. Changing the instance's **Variant** property updates the alert marker when Webstudio saves the MDX source.

Markdown Embed supports the same variants and rendered `data-state` values.

## Related

* [Markdown Embed](/university/core-components/markdown-embed) – Render GitHub-style alerts from Markdown
* [Content Block](/university/core-components/content-block) – Edit MDX as component instances
* [Blockquote](/university/core-components/blockquote) – Display quoted content
* [Paragraph](/university/core-components/paragraph) – Add body text


# Code Text

Display source code with language-aware syntax highlighting.

Use Code Text to display source code with syntax highlighting. It renders a semantic `<code>` element and preserves the source text for copying and screen readers.

## When to use

Use Code Text for code examples, commands, configuration, or other source text. Use [Text](/university/core-components/text) for prose that does not need syntax highlighting.

## How to use

1. Open the **Components** panel.
2. Expand **Typography**.
3. Drag **Code Text** onto the canvas.
4. Enter the source in **Text Content** in the Settings panel, or edit it on the canvas.
5. Select the matching **Language**.
6. Select a **Theme**.

**Text Content** opens a code editor that uses the selected language for syntax highlighting.

In Content mode, editors can change **Text Content** and **Language**. Change **Theme** in Design mode.

You can bind **Text Content**, **Language**, and **Theme** to variables or resource values. A fixed Language or Theme includes only that selected asset in the published build. A bound Language or Theme makes the available catalog part of the server build so any runtime value can be rendered. The browser loads only the language and theme selected at runtime.

The highlighted markup is rendered with SSR and SSG output, so it appears consistently when the page first loads and after it becomes interactive.

## Styling

Apply typography, spacing, border, and background styles in the Style panel. The selected theme supplies the syntax token colors and initial text and background colors. A background set in the Style panel overrides the theme background; select another theme to change the syntax colors.

## Related

* [Text](/university/core-components/text)
* [HTML Embed](/university/core-components/html-embed)
* [Paragraph](/university/core-components/paragraph)


# Form

Send data, files, and emails, or build search and filters without reloading the page.

Form is a custom Webstudio component for collecting information without reloading the page. Use it to:

* Send data and files to an API.
* Send email notifications and visitor confirmations.
* Build search and filter forms that update URL query parameters.

A single submission can do several things at once. For example, it can email your team, confirm receipt to the visitor, and send the submitted data to another service.

**You can test all of this in Preview without publishing.**

For a basic HTML form with native browser behavior, insert an [Element](/university/core-components/element) and set its tag to `form`.

## Start with a working contact form

Insert **Form (Beta)** from **Components > Forms**. On Webstudio Cloud, the default form is ready to use. It includes:

* Name, email, subject, and message fields.
* An optional file attachment.
* **Email to owner**, which sends the submitted information and page URL to your project recipients.
* **Email to visitor**, which sends a confirmation to the submitted email address.
* Editable success and error messages.

If you haven’t configured project recipients, the owner notification goes to the account holder.

Open Preview and submit with an email address you control to try it. These defaults are a starting point: you can change the fields, customize the emails, or replace the actions entirely.

## Collect the information you need

Add fields inside the Form from **Components > Forms**. Each field needs a **Name**, which identifies its value when the form is submitted.

<figure><picture><source srcset="/files/8pbZt3L9vKoyi4MwQeCJ" media="(prefers-color-scheme: dark)"><img src="https://330243581-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTep4EGj6hSC54ozHujyK%2Fuploads%2Fgit-blob-cdc44764d4cc2788fa36f0f8403b8f574b27e6e6%2Fform-components-light.jpg?alt=media" alt="Forms section in the Components panel, with Form (Beta), Button, Input Label, Text Input, File Input, Text Area, Select, Radio, and Checkbox."></picture><figcaption></figcaption></figure>

For example, a field named `subject` becomes available in bindings as `formData.subject`. Its visible label can be different.

Fields can share a Name to collect multiple values, such as a group of checkboxes.

See the individual component guides for field settings:

* [Text Input and File Input](/university/core-components/input)
* [Text Area](/university/core-components/textarea)
* [Select](/university/core-components/select)
* [Radio](/university/core-components/radio-button)
* [Checkbox](/university/core-components/checkbox)
* [Input Label](/university/core-components/label)
* [Button](/university/core-components/button)

## Send to several destinations

**Actions** define what happens when someone submits the Form. Each action uses a Resource, which contains the destination and its settings.

You can select up to **10 REST, GraphQL, or Email Resources**.

Select the Form and open **Settings > Properties & attributes > Action**:

* Click **+** to choose a Resource.
* Click an existing action to edit its Resource.
* Drag actions to change their order.
* Remove an action to stop using it on this Form. The Resource itself remains available.

<figure><picture><source srcset="/files/XRNKeM7hA8B9Lt5oApoz" media="(prefers-color-scheme: dark)"><img src="https://330243581-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTep4EGj6hSC54ozHujyK%2Fuploads%2Fgit-blob-925d615e71ca8c99139483f2c4ba4a0e8f3410aa%2Fform-actions-light.jpg?alt=media" alt="Form Action settings with the plus button, Email to owner and Email to visitor actions, and the Success redirect control below them."></picture><figcaption></figcaption></figure>

If the list is empty, create a Resource in **Variables** first. You can select each Resource once.

Actions run in parallel. Reordering them changes the order of their results, not the order in which they run. One selected action cannot use another action’s response.

### Use submitted values

Create a Resource **on the Form** when it needs submitted values. You can then bind fields into its settings:

```
"Contact form: " + (formData.subject ?? "New message")
```

Resources on ancestors or the global root can also be selected as actions, but they cannot bind this Form’s local variables.

### Send to an API

Choose a Resource type that matches your service:

* **REST:** configure the URL, headers, search parameters, body, and format.
* **GraphQL:** configure the URL, headers, query or mutation, and variables.

When used as Form actions, HTTP Resources send **POST** requests, regardless of their saved Method.

If an HTTP Resource has no configured body, the Form sends its submitted fields. This also works for Resources on ancestors or the global root. Set a custom body when you want to choose or transform the fields yourself. GraphQL uses its configured query and variables.

For REST Resources, **Format** controls how the body is sent:

| Format                | What it sends                                                                                                       |
| --------------------- | ------------------------------------------------------------------------------------------------------------------- |
| `auto`                | Plain text for text values, JSON for objects and arrays, or multipart form data when the body contains file fields. |
| `application/json`    | An object or array as JSON.                                                                                         |
| `multipart/form-data` | Fields and files as form data.                                                                                      |

Choose the format your API expects. Uploaded files need multipart form data; they cannot be sent as JSON.

See [Variables](/university/foundations/variables) for more about Resource settings and bindings.

## Customize email notifications

Email Resources can notify your team, confirm receipt to a visitor, or send different messages to different recipients. The default Form includes two emails, but you can edit or remove either one.

<figure><picture><source srcset="/files/3jZiplTj3G3SNKPAPhFb" media="(prefers-color-scheme: dark)"><img src="https://330243581-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTep4EGj6hSC54ozHujyK%2Fuploads%2Fgit-blob-b98e49225196c787d05df3adb8a69ba39f727e5e%2Fform-email-light.jpg?alt=media" alt="Email to owner Resource configuration with project recipients, an empty Reply to field, bound Subject and Body fields, and Attach submitted files selected."></picture><figcaption></figcaption></figure>

### Choose recipients

| Recipients option                 | Who receives the email                                                        |
| --------------------------------- | ----------------------------------------------------------------------------- |
| **Project recipients (or owner)** | Addresses from project settings, or the account holder if that list is empty. |
| **Custom recipients**             | Addresses entered or bound on this Resource.                                  |
| **Visitor email field**           | The address submitted through the Form’s selected email input.                |

Use `Name <address>` to include a recipient’s name, such as `John <john@example.com>`. Separate multiple custom recipients with commas.

For a visitor confirmation, create the Email Resource on the Form. Its email input must have a fixed **Name** and a **Type** of `email` to appear in the field selector. The submitted value must contain one valid email address.

<figure><picture><source srcset="/files/KoOK8PSgM8d9Lmg90OIk" media="(prefers-color-scheme: dark)"><img src="https://330243581-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTep4EGj6hSC54ozHujyK%2Fuploads%2Fgit-blob-8f18bcbe9ed3df266da0e29fd3725040df814159%2Fform-visitor-light.jpg?alt=media" alt="Email to visitor Resource with Visitor email field selected, the email input chosen, a confirmation message, and Do not attach files selected."></picture><figcaption></figcaption></figure>

### Set Reply to

**Reply to** determines where replies go. It accepts one address, optionally with a name, and supports bindings.

For an owner notification, you could use the visitor’s details so your team can reply directly:

```
`${formData.name} <${formData.email}>`
```

Leave Reply to empty on the Resource to use the value from project settings.

The name in Reply to also identifies your organization in the default visitor confirmation. It appears in the subject and at the end of the message. Without a name, the default uses “Site Owner.”

Reply to does not change the sending email address used by Webstudio’s email service.

### Write the subject and message

Both **Subject** and **Body** support bindings. Use submitted fields to personalize the message, or `browserInfo.pageUrl` to include the page it came from.

To include all submitted fields, insert `formData` into the body:

```
`Message from ${formData.name}

${formData}`
```

Webstudio turns the object into readable field names and values. Adding another named field then includes it automatically. Files appear as filenames; attaching the files is a separate setting.

Automatic formatting excludes password inputs and Webstudio’s internal fields. If an input’s Name or Type uses a binding, the automatic field list is omitted. Use explicit field bindings in that case.

The default visitor message thanks the visitor, confirms receipt, and ends with the name from Reply to. You can replace its subject and body with your own text.

Email bindings can use submitted fields and other available Variables, but cannot use another Resource’s response.

### Share defaults across forms

Set recipients and Reply to under **Project settings > Emails**.

* Choose **Project recipients** on a Resource to use the shared recipient list.
* Leave its **Reply to** empty to inherit the project value.
* Set either field on the Resource to override that default.

Subject and body belong to each Email Resource.

See [project email settings](/university/foundations/project-settings#emails).

## Send files

Add a **File Input** inside the Form and give it a **Name**. Use **Required**, **Accept**, and **Multiple** to configure the picker.

**Accept** helps visitors choose the right files. The receiving server must still validate their types.

Each action controls what happens to uploaded files:

* **Email:** choose **Attach submitted files** or **Do not attach files**.
* **HTTP API:** use multipart form data. The automatic format selects it when the body contains files.

The default owner email attaches files. The default visitor confirmation does not. An optional file input left empty sends no file.

See [Input](/university/core-components/input) for field settings and [Limits and hosting](#limits-and-hosting) below for upload limits.

## Build search and filter forms

Remove all actions to switch the Form to **Navigation mode**.

Submitting now puts the named field values into the current page’s URL without a full reload. For example, submitting `chairs` through a field named `query` on `/products` produces:

```
/products?query=chairs
```

A page Resource can use that value through a binding:

```
system.search.query
```

Bind it to the Resource’s search parameter to load matching results. Add more fields for filters such as category or price.

Submission replaces the current query string. Include any parameters you want to keep as fields; hidden inputs work for this. Fields sharing a Name produce repeated query parameters.

Navigation mode does not use **Success redirect**. Use Resource actions when you need to upload files.

## Choose what happens after submission

The default Form contains three containers:

* **Form content** holds the fields.
* **Success message** appears after success.
* **Error message** appears after failure.

Their **Show** bindings use `formState` to control visibility. By default, success hides the fields and shows the success message. Failure keeps the fields and entered values visible.

Select the message containers in Navigator to edit their text and design.

### Keep the fields visible

To keep the Form visible after success, select **Form content** and change its **Show** binding to an always-visible value.

A successful submission without a redirect resets fields to their default values. The default template has empty values, so visitors see an empty form again. You can show the success message alongside it.

On published dynamic sites, a successful submission also refreshes the current page’s Resources without reloading the page.

### Redirect to another page

Set **Success redirect** below the actions to send visitors to a thank-you page or another URL after success.

This setting is available when the Form has actions.

### Handle failures

All actions except visitor confirmation must succeed for the Form to show success. A failed visitor confirmation is recorded in `errors`, but does not make an otherwise successful submission fail.

Actions can succeed or fail independently. Your team might receive an email even when an API request fails. Check the individual results before submitting again.

Emails are not automatically resent when delivery is uncertain, to avoid duplicates.

## Use Form variables

The default Form provides these Variables on the Form and its children:

<figure><picture><source srcset="/files/oudKwmn9iRg9dkQ4zf6L" media="(prefers-color-scheme: dark)"><img src="https://330243581-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTep4EGj6hSC54ozHujyK%2Fuploads%2Fgit-blob-6d9c5663ea5abba9a8308fe8a27622a5744acf2b%2Fform-variables-light.jpg?alt=media" alt="Form Variables list showing formData, browserInfo, Email to owner, Email to visitor, formState, results, errors, and system, with dynamic and static badges."></picture><figcaption></figcaption></figure>

| Variable      | What it contains                                                                                 |
| ------------- | ------------------------------------------------------------------------------------------------ |
| `formData`    | Submitted values, keyed by field Name.                                                           |
| `browserInfo` | The page URL and available visitor details, such as browser, language, referrer, and IP address. |
| `formState`   | `initial`, `success`, or `error`.                                                                |
| `results`     | Action responses in order, including Resource name, HTTP status, and response body.              |
| `errors`      | Failed action details, including available status, response body, and error message.             |

Use `formData` and `browserInfo` to customize messages and requests. Use `results`, `errors`, and `formState` to display feedback after submission.

Create action Resources on the Form itself when they need these local bindings.

Available visitor details depend on the host and request. Referrer query parameters are removed, and an IP address is only included when the host provides a trusted source. `browserInfo` does not expose cookies, authorization, or raw request headers.

## Test without publishing

Preview lets you test the current draft, including emails, API requests, attachments, and redirects.

1. Open **Preview** and submit the Form.
2. Check the visible feedback and expected emails or API results.
3. Return to design mode and open an action’s Resource.

**Preview sends real emails and makes real requests.** Use addresses you control and test destinations.

The Resource inspector shows:

* **Request:** what was sent.
* **Response:** the returned status, headers, and body, where available.
* **Diagnostics:** additional execution details, when available.

Sensitive values are hidden, large output is limited, and files appear as metadata rather than their full contents.

Loading an Email Resource’s request preview does not send an email. Submit the Form in Preview to test delivery and capture its response.

Once the Form works as expected, publish it to update the site. Later Builder changes need to be published again.

## Limits and hosting

### Files and message sizes

Limits count the whole request or message, not each file separately.

| Limit                                      | What it includes                                                                                                          |
| ------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------- |
| **25 MiB per published Form submission**   | The complete incoming multipart request: fields, files, and multipart encoding.                                           |
| **4.5 MB per Preview Form submission**     | The complete request: fields, files, and multipart encoding. Leave room for fields and encoding when choosing file sizes. |
| **25 MiB per outbound HTTP Resource body** | Webstudio's body-size check for a Form action. The destination or its network path may allow less.                        |
| **32 attachments per email**               | All files attached to that email.                                                                                         |
| **7 MiB per email-service request**        | The complete JSON request, including email text, attachment data encoded as Base64, and other fields.                     |

Multipart encoding adds bytes around each uploaded file and field. Base64 encoding makes attachments larger in the email-service request. Leave room for both when choosing file sizes.

[Cloudflare's email limits](https://developers.cloudflare.com/email-service/platform/limits/) list 5 MiB for a complete email, or 25 MiB for verified destination addresses. Those are provider limits, not guaranteed Form allowances: Webstudio's 7 MiB JSON limit and the incoming Form limit can stop a submission first. The final email includes its own encoding and headers.

### Sending limits

Webstudio Cloud allows up to **five team-recipient deliveries per submission**, plus one visitor confirmation Resource. Duplicate addresses count as separate deliveries.

An approximate limit of **50 recipient deliveries per project, per Cloudflare location, per minute** also applies. This is not a guaranteed cutoff. Preview and published sites have separate limits. Provider account limits may apply too.

If an email fails, check its response for the reported reason. See [Cloudflare’s email error reference](https://developers.cloudflare.com/email-service/api/send-emails/workers-api/#error-codes).

### Hosting

Resource actions need a server. Webstudio Cloud provides this; a static export cannot run them.

Self-hosted sites need a supported server setup and an email integration to send Email Resources. See [self-hosting](/university/self-hosting).

## Related

* [Variables](/university/foundations/variables) — Resources, scope, and bindings.
* [Project settings](/university/foundations/project-settings#emails) — Shared recipients and Reply to.
* [Input](/university/core-components/input) — Field types and settings.
* [Element](/university/core-components/element) — Basic HTML forms.


# Webhook Form (deprecated)

Deprecated. Existing Webhook Forms send submissions to an email address or optionally third-party services like Airtable, n8n, or Zapier.

{% hint style="info" %}
**Deprecated:** Webhook Form remains supported through the next release and will be removed afterward. Use the new [Form](/university/core-components/form) for Resource actions or URL-based navigation, or an **Element** with tag `form` for native browser form behavior.
{% endhint %}

{% embed url="<https://www.youtube.com/watch?v=eE-CkewQHMs>" %}

## Receiving form submissions

Webhook Forms are used when you need to send form submission data to an external service, rather than modifying page content like searches and filters.

### Email notifications

By default, submissions are sent to the project owner.

{% hint style="info" %}
**Pro feature:** You can customize the recipient of email notifications by navigating to **Project settings > Emails**.
{% endhint %}

### Webhooks

You can also send form submission data to a webhook—an external URL that receives the data and triggers an action, such as adding a contact to an email automation platform.

1. Obtain a webhook URL from a third-party platform such as [Airtable](/university/integrations/airtable-form-webhook).
2. Paste the URL into the `Action` field in **Webhook Form > Settings**.

Once set up, every form submission will send a payload (form fields and values) to the webhook URL.

## Using the Webhook Form component

Existing projects can keep and edit their saved Webhook Form instances. The component is no longer available for new insertion; use the new [Form](/university/core-components/form) for new Resource submissions.

{% hint style="warning" %}
Webhook Forms do not submit inside the Builder, including in Preview. They only submit on the published site.
{% endhint %}

### Webhook Form structure

A Webhook Form consists of three nested instances:

1. **Form content** – The primary form fields.
2. **Success message** – Displayed upon successful submission.
3. **Error message** – Shown when an error occurs.

You can [add new inputs](/university/core-components/form#collect-the-information-you-need) to further expand and modify your form.

### Form states

Webhook Forms automatically switch between states based on submission results.

#### Success message

When a submission is successful, users will see a success message. To customize it:

1. Select the main "Form" instance and go to **Settings**.
2. Change the **State** from "Initial" to "Success."
3. Edit the success message directly on the canvas.

#### Error message

If there’s an error during submission, users will see an error message. To modify it:

1. Select the Webhook Form component.
2. Set the **State** to "Error" to preview and edit the error message.

## Form inputs

{% hint style="warning" %}
Each input field must have a `name` attribute for its data to appear in email notifications and webhook payloads.
{% endhint %}

Ensure every form input has a value for the `name` field to be included in submissions.

<figure><img src="https://330243581-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTep4EGj6hSC54ozHujyK%2Fuploads%2Fgit-blob-752e480fc75b319faf7475d7ad1505ff27644bc6%2Fform-name.png?alt=media" alt="Form input name"><figcaption></figcaption></figure>

For a full list of input types, including checkboxes and radio buttons, refer to [Form inputs](/university/core-components/form#collect-the-information-you-need).

### Input properties

Each input field has several configurable properties in Settings:

* **Name**: The field identifier used in submissions (required for data to appear)
* **Type**: Define the input type (text, email, tel, etc.). Setting type to "email" enforces email format validation.
* **Placeholder**: Hint text shown inside the input before user enters data (e.g., "<john@doe.com>")
* **Required**: When enabled, the form cannot be submitted without this field
* **Autofocus**: When enabled, this field is automatically focused when the page loads

### Styling form states

You can create interactive form styling using states in the Style panel:

* **Hover**: Apply styles when users hover over an input (e.g., wider border, box shadow)
* **Focus**: Apply styles when an input is active/selected (e.g., colored border, glow effect)
* **Placeholder**: Style the placeholder text appearance

To apply state-specific styles, select the input element, open the state dropdown in the Style panel, and choose the state you want to customize.

## Bot protection

Webstudio forms include built-in bot protection to prevent spam submissions. This protection works automatically without requiring any additional configuration – no CAPTCHAs needed. The system analyzes submission patterns to distinguish between legitimate users and automated bots.

## Related

* [Form](/university/core-components/form) – Submit to configured Resources
* [Input](/university/core-components/input) – Text input fields
* [Button](/university/core-components/button) – Submit buttons
* [n8n Integration](/university/integrations/n8n) – Automate form workflows
* [Zapier Integration](/university/integrations/zapier) – Connect to other apps


# Button

Add clickable buttons to your Webstudio site with the Button component.

> See [MDN: \<button>](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/button)

The Button component creates clickable buttons for user interactions. Buttons trigger actions like submitting forms, opening dialogs, or navigating when combined with other components.

## When to Use

Use Button for:

* Form submissions
* Triggering actions (open dialog, toggle, etc.)
* Call-to-action elements
* Interactive controls

{% hint style="warning" %}
Buttons are for actions, not navigation. For navigation links, use the [Link](/university/core-components/link) component instead. If you need a link styled as a button, style the Link component accordingly.
{% endhint %}

## How to Use

1. Drag a **Button** component from Components > Forms onto your canvas
2. Edit the button text
3. Style using the Style Panel
4. Place inside a Form for submissions, or use HTML Embed scripts for custom interactivity

## Properties

Some commonly used properties (see the Settings panel for all available options):

| Property     | Description                                                                 |
| ------------ | --------------------------------------------------------------------------- |
| **type**     | `submit` (form submission), `reset` (clear form), or `button` (general use) |
| **disabled** | Prevents interaction when true                                              |
| **name**     | Name for form submission                                                    |
| **value**    | Value for form submission                                                   |

## Button Types

### Submit Button

Submits the parent form when clicked. Use inside [Form](/university/core-components/form), or inside an existing [deprecated Webhook Form](/university/core-components/webhook-form).

### Reset Button

Clears all form fields to their default values.

### Button (Default)

General purpose button for non-form actions. Use with component interactions like opening dialogs.

## Styling States

Some common states (you can also create custom states):

* **Default** - Normal appearance
* **Hover** (`:hover`) - Mouse is over the button
* **Focus** (`:focus-visible`) - Keyboard focused
* **Active** (`:active`) - Being pressed
* **Disabled** (`:disabled`) - Cannot be clicked

## Related

* [Form](/university/core-components/form) – Form containers for submissions
* [Link](/university/core-components/link) – Navigation links
* [Input](/university/core-components/input) – Text input fields


# Label

Add accessible labels to form inputs in Webstudio.

> See [MDN: \<label>](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/label)

The **Label** component creates an HTML `<label>` element that provides an accessible name for form controls.

## Overview

Labels are essential for form accessibility. They tell users what information to enter and allow clicking the label to focus its associated input.

## Properties

| Property | Type   | Description                       |
| -------- | ------ | --------------------------------- |
| `for`    | string | ID of the associated form control |
| `id`     | string | Unique identifier                 |
| `class`  | string | CSS class names                   |

## Associating Labels with Inputs

### Method 1: Using `for` Attribute

Match the label's `for` attribute to the input's `id`:

```
Label (for: email)
  └── "Email Address"
Input (id: email, name: email, type: email)
```

### Method 2: Wrapping

Place the form control inside the label:

```
Label
  ├── "Email Address"
  └── Input (name: email, type: email)
```

## Benefits of Labels

1. **Accessibility**: Screen readers announce the label when the input is focused
2. **Usability**: Clicking the label focuses the associated input
3. **Touch targets**: Increases the clickable area on mobile devices

## Styling Labels

### Typography

* **Font Weight**: Slightly bolder than body text
* **Font Size**: Same or slightly smaller than inputs
* **Color**: High contrast for readability

### Layout

* **Position**: Above input (most common) or beside it
* **Spacing**: Add margin between label and input
* **Alignment**: Align with input edges

### Required Field Indicators

Show required fields with visual indicators:

```
Label (for: email)
  ├── "Email Address"
  └── Span (class: required, color: red)
      └── "*"
```

## Form Layout Patterns

### Stacked Labels (Most Common)

```
Box
├── Label → "Name"
├── Input
├── Label → "Email"
├── Input
└── Button
```

### Inline Labels

```
Box (display: flex, align-items: center)
├── Label (width: 100px) → "Name"
└── Input (flex: 1)
```

### Floating Labels

Create floating label effect with CSS positioning:

1. Position label absolutely over input
2. On input focus/filled, translate label up
3. Reduce font size when floating

## Checkbox and Radio Labels

For checkboxes and radio buttons, wrap or associate labels:

```
Label (display: flex, align-items: center, gap: 8px)
├── Checkbox (or RadioButton)
└── "Accept terms and conditions"
```

This makes the entire label clickable.

## Best Practices

1. **Always use labels**: Every form control needs a label
2. **Be descriptive**: Clearly describe expected input
3. **Keep it concise**: Short, clear labels work best
4. **Mark required fields**: Indicate which fields are required
5. **Don't rely on placeholders**: Placeholders disappear, labels don't

## Error Messages

Associate error messages with the field:

```
Label (for: email) → "Email"
Input (id: email)
Text (role: alert) → "Please enter a valid email"
```

## Accessibility Tips

* Use proper label associations
* Include required field indicators in label text for screen readers
* Group related fields with fieldset and legend
* Ensure sufficient color contrast

## Related Components

* [Input](/university/core-components/input) - Text input fields
* [Textarea](/university/core-components/textarea) - Multi-line text input
* [Checkbox](/university/core-components/checkbox) - Checkbox input
* [RadioButton](/university/core-components/radio-button) - Radio button input
* [Form](/university/core-components/form) - Form container


# Input

Add text input fields to forms in Webstudio.

> See [MDN: \<input>](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input)

The **Input** component creates an HTML `<input>` element for collecting user data in forms.

## Overview

Input is a fundamental form control used to collect various types of user data including text, numbers, emails, passwords, and more.

## Properties

| Property       | Type    | Description                                      |
| -------------- | ------- | ------------------------------------------------ |
| `type`         | string  | Input type (text, email, password, number, etc.) |
| `name`         | string  | Form field name (used in form submission)        |
| `placeholder`  | string  | Hint text shown when empty                       |
| `value`        | string  | Current input value                              |
| `required`     | boolean | Whether the field is required                    |
| `disabled`     | boolean | Whether the input is disabled                    |
| `readonly`     | boolean | Whether the input is read-only                   |
| `autocomplete` | string  | Browser autocomplete behavior                    |
| `id`           | string  | Unique identifier                                |
| `class`        | string  | CSS class names                                  |

## Input Types

| Type             | Use Case                          |
| ---------------- | --------------------------------- |
| `text`           | General text input (default)      |
| `email`          | Email addresses (with validation) |
| `password`       | Hidden text entry                 |
| `number`         | Numeric values                    |
| `tel`            | Phone numbers                     |
| `url`            | Website URLs                      |
| `search`         | Search queries                    |
| `date`           | Date picker                       |
| `time`           | Time picker                       |
| `datetime-local` | Date and time                     |
| `file`           | File uploads                      |
| `hidden`         | Hidden form data                  |

## Form Usage

Place inputs within a [Form](/university/core-components/form). Existing projects can also keep inputs within a [deprecated Webhook Form](/university/core-components/webhook-form):

```
Form
├── Label (for: email-input)
├── Input (id: email-input, name: email, type: email)
├── Label (for: password-input)
├── Input (id: password-input, name: password, type: password)
└── Button (type: submit)
```

## Associating Labels

Always associate inputs with labels for accessibility:

### Method 1: Matching IDs

1. Set `id` on the Input (e.g., "email")
2. Set `for` on the Label (e.g., "email")

### Method 2: Wrapping

Place the Input inside a Label component.

## Validation

### Built-in Validation

HTML5 provides built-in validation for certain input types:

* `email` validates email format
* `url` validates URL format
* `number` validates numeric input

### Required Fields

Set `required: true` to make a field mandatory.

### Pattern Matching

Use the `pattern` attribute for custom validation with regex.

## Styling States

Style different input states:

| State       | Selector        | Use                    |
| ----------- | --------------- | ---------------------- |
| Default     | -               | Normal state           |
| Focus       | `:focus`        | When input is selected |
| Hover       | `:hover`        | Mouse over             |
| Disabled    | `:disabled`     | When disabled          |
| Invalid     | `:invalid`      | Validation failed      |
| Valid       | `:valid`        | Validation passed      |
| Placeholder | `::placeholder` | Placeholder text       |

### Example Styles

* **Border**: `1px solid #ccc`, change on focus
* **Padding**: `8px 12px` for comfortable typing
* **Border Radius**: Slight rounding looks modern
* **Focus Ring**: Use outline or box-shadow for accessibility

## Best Practices

1. **Always use labels**: Every input needs an associated label
2. **Use appropriate types**: Use `email` for emails, `tel` for phones, etc.
3. **Provide placeholders wisely**: Don't use placeholder as label replacement
4. **Show validation feedback**: Indicate errors clearly
5. **Ensure touch targets**: Minimum 44x44px for mobile
6. **Support autocomplete**: Use proper `autocomplete` values

## Autocomplete Values

Common `autocomplete` values:

| Value              | Use                |
| ------------------ | ------------------ |
| `name`             | Full name          |
| `email`            | Email address      |
| `tel`              | Phone number       |
| `street-address`   | Street address     |
| `postal-code`      | ZIP/postal code    |
| `country`          | Country            |
| `cc-number`        | Credit card number |
| `current-password` | Current password   |
| `new-password`     | New password       |

## Related Components

* [Form](/university/core-components/form) - Form container
* [Webhook Form](/university/core-components/webhook-form) - Deprecated form for existing webhook submissions
* [Label](/university/core-components/label) - Input labels
* [Textarea](/university/core-components/textarea) - Multi-line text input
* [Select](/university/core-components/select) - Dropdown selection

## Related Videos

{% embed url="<https://www.youtube.com/watch?v=SOd7V8A9SOw>" %}


# Text Area

Add multi-line text input fields to forms in Webstudio.

> See [MDN: \<textarea>](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/textarea)

The **Textarea** component creates a multi-line text input field for collecting longer text content.

## Overview

Textarea is used when you need users to enter multiple lines of text, such as messages, descriptions, comments, or any long-form content.

## Properties

| Property      | Type    | Description                    |
| ------------- | ------- | ------------------------------ |
| `name`        | string  | Form field name                |
| `placeholder` | string  | Hint text shown when empty     |
| `value`       | string  | Current content                |
| `required`    | boolean | Whether the field is required  |
| `disabled`    | boolean | Whether input is disabled      |
| `readonly`    | boolean | Whether input is read-only     |
| `rows`        | number  | Visible number of lines        |
| `cols`        | number  | Visible width in characters    |
| `maxlength`   | number  | Maximum character limit        |
| `minlength`   | number  | Minimum character limit        |
| `wrap`        | string  | Text wrapping mode (soft/hard) |
| `id`          | string  | Unique identifier              |
| `class`       | string  | CSS class names                |

## Basic Usage

```
Form
├── Label (for: message)
├── Textarea (id: message, name: message, rows: 5)
└── Button (type: submit)
```

## Sizing

### Fixed Size

Set `rows` and `cols` attributes for fixed dimensions:

* `rows`: Number of visible text lines
* `cols`: Width in average character widths

### CSS Sizing

For responsive sizing, use CSS:

* `width`: Control width (e.g., `100%`)
* `height`: Control height
* `min-height`/`max-height`: Set boundaries

### Resizable Behavior

By default, textareas can be resized by users. Control this with CSS:

* `resize: none` - Not resizable
* `resize: vertical` - Only vertical resizing
* `resize: horizontal` - Only horizontal resizing
* `resize: both` - Both directions (default)

## Character Limits

### Maximum Length

```
Textarea (maxlength: 500)
```

### Show Character Count

Use a Text component with an expression to show remaining characters:

```
Remaining: {500 - textareaValue.length}
```

## Styling

### Common Styles

* **Border**: Match your design system's input styles
* **Padding**: `12px` for comfortable text entry
* **Font**: Use the same font as your text inputs
* **Line Height**: 1.5 for readability

### State Styles

| State    | Style Suggestion                  |
| -------- | --------------------------------- |
| Default  | Subtle border, light background   |
| Focus    | Highlighted border or ring        |
| Disabled | Muted colors, cursor: not-allowed |
| Invalid  | Red border, error colors          |

## Best Practices

1. **Use labels**: Always associate with a label
2. **Set appropriate rows**: Show enough lines for expected content
3. **Provide size hints**: Let users know expected length
4. **Consider auto-resize**: Use JavaScript for auto-growing textareas
5. **Match input styles**: Keep consistent with other form fields
6. **Add character count**: For limited fields, show remaining characters

## When to Use

| Use Textarea           | Use Input          |
| ---------------------- | ------------------ |
| Comments/messages      | Single-line fields |
| Descriptions           | Names, emails      |
| Bio/about sections     | Phone numbers      |
| Addresses (multi-line) | Short inputs       |
| Notes                  | Passwords          |

## Related Components

* [Input](/university/core-components/input) - Single-line text input
* [Form](/university/core-components/form) - Form container
* [Label](/university/core-components/label) - Input labels


# Select

Add dropdown select inputs to forms in Webstudio.

> See [MDN: \<select>](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/select)

The native **Select** component creates an HTML `<select>` dropdown for choosing one option from a list.

## Overview

The native Select component uses the browser's built-in dropdown functionality. For a fully customizable dropdown with complete styling control, see the [Radix Select](/university/radix/select) component.

## Components

### Select

The dropdown container.

| Property   | Type    | Description                   |
| ---------- | ------- | ----------------------------- |
| `name`     | string  | Form field name               |
| `value`    | string  | Currently selected value      |
| `required` | boolean | Whether selection is required |
| `disabled` | boolean | Whether select is disabled    |
| `multiple` | boolean | Allow multiple selections     |
| `id`       | string  | Unique identifier             |
| `class`    | string  | CSS class names               |

### Option

Individual options within the select.

| Property   | Type    | Description                           |
| ---------- | ------- | ------------------------------------- |
| `value`    | string  | Value submitted when selected         |
| `selected` | boolean | Whether this option is selected       |
| `disabled` | boolean | Whether option is disabled            |
| `label`    | string  | Display text (alternative to content) |

## Basic Usage

```
Label (for: country)
  └── "Country"
Select (id: country, name: country)
├── Option (value: "") → "Select a country..."
├── Option (value: us) → "United States"
├── Option (value: uk) → "United Kingdom"
├── Option (value: ca) → "Canada"
└── Option (value: au) → "Australia"
```

## Placeholder Option

Create a placeholder with an empty first option:

```
Option (value: "", disabled: true, selected: true)
  └── "Please select..."
```

This shows hint text but can't be submitted.

## Form Submission

When submitted, the selected option's `value` is sent:

```
country=us
```

## Styling Limitations

Native select elements have limited styling options:

* **Can style**: border, background, padding, font
* **Cannot style**: dropdown arrow, option appearance (varies by browser/OS)

For full styling control, use the [Radix Select](/university/radix/select) component.

### Basic Styling

```css
/* What you can customize */
Select {
  border: 1px solid #ccc;
  border-radius: 4px;
  padding: 8px 12px;
  background: white;
  font-size: 16px;
}
```

## Multiple Selection

For selecting multiple options:

```
Select (name: languages, multiple: true)
├── Option (value: en) → "English"
├── Option (value: es) → "Spanish"
├── Option (value: fr) → "French"
└── Option (value: de) → "German"
```

Users can Ctrl/Cmd+click to select multiple options.

## Accessibility

1. **Use labels**: Always associate with a label
2. **Include placeholder**: Help users understand what to select
3. **Keyboard support**: Native select works with keyboard
4. **Avoid disabled options**: Can be confusing for screen readers

## Best Practices

1. **Use for 5+ options**: Use radio buttons for fewer options
2. **Alphabetize long lists**: Or use logical ordering
3. **Include placeholder**: Show default unselected state
4. **Keep options concise**: Long text truncates
5. **Group related options**: Use optgroup for categories (via HTML Embed if needed)

## When to Use Native vs Radix Select

| Native Select             | Radix Select          |
| ------------------------- | --------------------- |
| Simple forms              | Custom styling needed |
| Mobile-friendly           | Desktop-focused       |
| Quick implementation      | Searchable options    |
| Standard browser behavior | Complex interactions  |
| Works everywhere          | Advanced features     |

## Dynamic Options

Populate options from data:

```
Select (name: category)
└── Collection (data: categories)
    └── Option (value: category.id)
        └── {category.name}
```

## Related Components

* [Radix Select](/university/radix/select) - Fully customizable select
* [Radio Button](/university/core-components/radio-button) - For fewer options
* [Label](/university/core-components/label) - Associated labels
* [Form](/university/core-components/form) - Form container


# Checkbox

Add checkbox inputs to forms in Webstudio.

> See [MDN: \<input type="checkbox">](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/checkbox)

The **Checkbox** component creates an HTML checkbox input for boolean selections.

## Overview

Checkboxes allow users to select one or more options from a set, or toggle a single option on/off.

## Properties

| Property   | Type    | Description                     |
| ---------- | ------- | ------------------------------- |
| `name`     | string  | Form field name                 |
| `value`    | string  | Value submitted when checked    |
| `checked`  | boolean | Whether the checkbox is checked |
| `required` | boolean | Whether selection is required   |
| `disabled` | boolean | Whether input is disabled       |
| `id`       | string  | Unique identifier               |
| `class`    | string  | CSS class names                 |

## Basic Usage

### Single Checkbox

For a single yes/no option:

```
Label (display: flex, align-items: center, gap: 8px)
├── Checkbox (name: newsletter, value: yes)
└── "Subscribe to newsletter"
```

### Checkbox Group

For multiple selections:

```
Box
├── Label
│   ├── Checkbox (name: interests, value: design)
│   └── "Design"
├── Label
│   ├── Checkbox (name: interests, value: development)
│   └── "Development"
└── Label
    ├── Checkbox (name: interests, value: marketing)
    └── "Marketing"
```

When submitted, all checked values are sent with the same `name`.

## Styling Checkboxes

### Native Styling

Browser checkboxes can be styled with:

* `accent-color`: Change the checked color
* `width`/`height`: Adjust size
* `cursor: pointer`: Show clickable cursor

### Custom Checkbox

For fully custom checkboxes, consider using the [Radix Checkbox](/university/radix/checkbox) component, which provides complete styling control while maintaining accessibility.

## Form Submission

### Single Checkbox

If checked, sends: `name=value` If unchecked, nothing is sent

### Multiple Checkboxes (same name)

Sends all checked values: `interests=design&interests=development`

## Validation

### Required Checkbox

For required checkboxes (like terms acceptance):

```
Label
├── Checkbox (name: terms, required: true)
└── "I agree to the terms and conditions"
```

The form won't submit until the checkbox is checked.

## Accessibility

1. **Always use labels**: Associate every checkbox with a label
2. **Group related checkboxes**: Use fieldset and legend for groups
3. **Provide clear labels**: Make options self-explanatory
4. **Keyboard support**: Ensure checkboxes work with Space key

### Grouping with Fieldset

```
Box (role: group)
├── Text (role: legend) → "Notification Preferences"
├── Label
│   ├── Checkbox (name: notify, value: email)
│   └── "Email notifications"
└── Label
    ├── Checkbox (name: notify, value: sms)
    └── "SMS notifications"
```

## States

Style different checkbox states:

| State         | Description                         |
| ------------- | ----------------------------------- |
| Unchecked     | Default empty state                 |
| Checked       | Selected state                      |
| Disabled      | Cannot be interacted with           |
| Focus         | Keyboard focus visible              |
| Indeterminate | Partial selection (JavaScript only) |

## Best Practices

1. **Use for multiple selections**: Use checkboxes when users can select multiple options
2. **Label clickable**: Make entire label area clickable
3. **Vertical alignment**: Stack checkboxes vertically for easier scanning
4. **Reasonable defaults**: Pre-check options when appropriate
5. **Clear labeling**: Use positive phrasing (what happens when checked)

## When to Use

| Use Checkbox        | Use Radio                  | Use Toggle       |
| ------------------- | -------------------------- | ---------------- |
| Multiple selections | Single selection from many | On/off setting   |
| Optional agreements | Required choice            | Immediate effect |
| Filter options      | Exclusive options          | Settings         |

## Related Components

* [RadioButton](/university/core-components/radio-button) - Single selection from multiple options
* [Switch](/university/radix/switch) - Toggle on/off (Radix)
* [Radix Checkbox](/university/radix/checkbox) - Fully customizable checkbox
* [Label](/university/core-components/label) - Associated labels
* [Form](/university/core-components/form) - Form container


# Radio Button

Add radio button inputs for single-choice selections in Webstudio forms.

> See [MDN: \<input type="radio">](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/radio)

The **Radio Button** component creates an HTML radio input for selecting one option from a group.

## Overview

Radio buttons are used when users must select exactly one option from a predefined set. Unlike checkboxes, selecting one radio button in a group automatically deselects any previously selected option.

## Properties

| Property   | Type    | Description                       |
| ---------- | ------- | --------------------------------- |
| `name`     | string  | Group name (same for all options) |
| `value`    | string  | Value submitted when selected     |
| `checked`  | boolean | Whether this option is selected   |
| `required` | boolean | Whether a selection is required   |
| `disabled` | boolean | Whether input is disabled         |
| `id`       | string  | Unique identifier                 |
| `class`    | string  | CSS class names                   |

## Basic Usage

### Radio Group

Radio buttons with the same `name` form a group:

```
Box (role: radiogroup)
├── Label
│   ├── RadioButton (name: plan, value: free)
│   └── "Free Plan"
├── Label
│   ├── RadioButton (name: plan, value: pro)
│   └── "Pro Plan"
└── Label
    ├── RadioButton (name: plan, value: enterprise)
    └── "Enterprise Plan"
```

Only one option can be selected at a time.

## Important Rules

1. **Same name**: All radio buttons in a group must share the same `name`
2. **Unique values**: Each option needs a unique `value`
3. **At least one label**: Each radio button needs an associated label

## Styling Radio Buttons

### Native Styling

Basic styling options:

* `accent-color`: Change the selected color
* `width`/`height`: Adjust size
* `cursor: pointer`: Show clickable cursor

### Custom Radio Buttons

For fully custom styling, consider using the [Radix Radio Group](/university/radix/radio-group) component, which provides complete design control.

## Form Submission

When the form is submitted, the selected option's value is sent:

```
plan=pro
```

If nothing is selected and the field is required, the form won't submit.

## Default Selection

To pre-select an option, set `checked: true`:

```
RadioButton (name: plan, value: pro, checked: true)
```

Consider pre-selecting the most common choice.

## Accessibility

### Grouping

Properly group radio buttons:

```
Box (role: radiogroup, aria-label: "Subscription Plan")
├── Label → RadioButton + "Option 1"
├── Label → RadioButton + "Option 2"
└── Label → RadioButton + "Option 3"
```

### Keyboard Navigation

Radio groups support keyboard navigation:

* **Arrow keys**: Move between options
* **Space**: Select focused option
* **Tab**: Move to/from the group

## Layout Patterns

### Vertical Stack (Recommended)

```
Box (display: flex, flex-direction: column, gap: 8px)
├── Label (display: flex, gap: 8px)
│   ├── RadioButton
│   └── "Option 1"
```

### Horizontal Row

```
Box (display: flex, gap: 16px)
├── Label
│   ├── RadioButton
│   └── "Yes"
└── Label
    ├── RadioButton
    └── "No"
```

### Card Selection

```
Label (padding: 16px, border: 1px solid, border-radius: 8px)
├── Box (display: flex, gap: 12px)
│   ├── RadioButton
│   └── Box
│       ├── Text (bold) → "Plan Name"
│       └── Text → "Description"
```

## States

| State     | Description               |
| --------- | ------------------------- |
| Unchecked | Default unselected state  |
| Checked   | Selected state            |
| Disabled  | Cannot be interacted with |
| Focus     | Keyboard focus visible    |

## Best Practices

1. **Use for mutually exclusive choices**: Only when one option must be selected
2. **Vertical layout**: Easier to scan than horizontal
3. **Logical order**: Most common or recommended first
4. **Clear labels**: Self-explanatory option text
5. **Default selection**: Pre-select when there's a logical default
6. **5-7 options max**: Use select dropdown for more options

## When to Use

| Use Radio Buttons              | Use Checkbox                | Use Select        |
| ------------------------------ | --------------------------- | ----------------- |
| 2-7 mutually exclusive options | Multiple selections allowed | Many options (8+) |
| All options visible            | Toggle single option        | Space constrained |
| User needs to compare          | Independent choices         | Long option text  |

## Related Components

* [Checkbox](/university/core-components/checkbox) - Multiple selections
* [Select](/university/core-components/select) - Dropdown single selection
* [Radix Radio Group](/university/radix/radio-group) - Customizable radio buttons
* [Label](/university/core-components/label) - Associated labels
* [Form](/university/core-components/form) - Form container


# Image

In this article, we will learn how to render optimized, responsive images for a site using the Image Component in Webstudio.

> See [MDN: \<img>](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/img)

{% embed url="<https://www.youtube.com/watch?v=o2tEwZ_zeUI>" %}

***

## How to use the Image component

The Image component is available under **Components > Media**. Drag it onto the canvas or click it to insert it at the selected location.

<picture><source srcset="/files/GW27fUw54Kz1uYwqQkZZ" media="(prefers-color-scheme: dark)"><img src="https://330243581-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTep4EGj6hSC54ozHujyK%2Fuploads%2Fgit-blob-d83bd1cac27b29a5fd037b3f8d6a27521380d365%2Fcomponents-panel-light.png?alt=media" alt="Components panel with Image in the expanded Media group"></picture>

### Responsive images

Webstudio automatically generates responsive images for your website, so you don't have to worry about manually resizing and optimizing images for different devices.

These responsive images automatically adjust their size based on the user's screen size, ensuring optimal resolution and quick loading whether the user is on a desktop monitor or a smartphone.

### Supported image formats

All images in Webstudio are automatically optimized and served in WebP or AVIF format, depending on the responsive image resolution and browser support. This eliminates the need for pre-optimization and extra tools to compress or convert images.

WebP is supported by all modern browsers, including Chrome, Edge, and Firefox, while AVIF offers better compression quality and smaller file sizes but lacks support in browsers like Edge and Internet Explorer.

### Ensure a stable layout for improved performance

It is essential to ensure that your browser can calculate an image's width and height without waiting for it to load. You can do this by setting the image's width/height in the Style panel or directly in the Image instance's properties.

When you set an image's width and aspect ratio, your browser can calculate its height. You can define the width, height, and aspect ratio in the parent element's Style panel, or it can be hinted at by the layout around the image.

If a browser can't calculate an image's size, it continues rendering the page, but the image element remains collapsed. Once the browser figures out the size of the unhinted image, it renders that image and recalculates every component on the site. This can create a glitch-like effect on the live site and negatively impact site performance.

> Note: You can assess page speed using tools like [Page Speed](https://pagespeed.web.dev/). For reference, an unhinted hero image can easily decrease the page speed score from 100 to 70!

### Provide aspect ratio upfront

In Webstudio, every image has its aspect ratio set automatically. To prevent any layout shifts on your site, it's best to set the "width" property for each image you add. This ensures that the images fit smoothly into the overall design without causing any unexpected changes in layout.

***

## How to customize an Image instance's properties

You can customize the properties of an Image instance by selecting it and going to "Settings." Here is an overview of each property:

### Source

The Source property lets you link an image file from the Assets Panel to your image instance. To add an image to your instance, click "Choose source" and select an image from the Assets Panel.

If your image is not in the Assets Panel, you can upload it from your computer files.

You can also drag a single image from the Assets panel directly onto the canvas. Webstudio inserts a new Image component and selects the dragged asset as its source.

### Width and height

The "width" and "height" properties define your image's initial display size in pixels and its aspect ratio. However, the final rendered image size is subject to CSS rules and layout constraints.

This means that if you set the width or height of an image in the Style panel or the layout limits the space for the image, the Width/Height properties for the Image instance will be ignored.

The Width/Height properties are prioritized in some cases. For instance, if you only set the image's height in the Style panel, the width will be automatically determined from the Image Instance's "Width" property, and vice versa. This maintains the aspect ratio while adjusting the image's size with your specified values.

### Alternative text (alt)

The "alt" property provides alternative text for the image. This text is vital for accessibility purposes, as it describes the image's content in case the image cannot be seen.

### Loading

The "Loading" property specifies how the browser handles image loading. You can set it to "Lazy" to load the image only when it comes into the viewport or "Eager" to load it immediately.

Here's a helpful tip: If you have an image in the initial viewport, always set it to "Eager." This way, the image gets priority, and it loads quickly, improving your site's overall performance.

### Optimize

The "optimize" property lets you control if the image will go through the image transformation process including resizing, converting, and compressing it. This setting works on first and third-party images (e.g., images that come from a headless CMS). In rare scenarios, the user may want to turn this off.

## Related

* [Vimeo](/university/core-components/vimeo) – Embed Vimeo videos
* [YouTube](/university/core-components/youtube) – Embed YouTube videos
* [Link](/university/core-components/link) – Make images clickable


# Video

Embed self-hosted videos in Webstudio pages.

> See [MDN: \<video>](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/video)

The **Video** component embeds an HTML5 `<video>` element for playing self-hosted video files.

## Overview

Use the Video component when you have your own video files to host, rather than embedding from platforms like YouTube or Vimeo. For platform embeds, use the [YouTube](/university/core-components/youtube) or [Vimeo](/university/core-components/vimeo) components instead.

## Properties

| Property      | Type    | Description                            |
| ------------- | ------- | -------------------------------------- |
| `src`         | string  | URL to the video file                  |
| `poster`      | string  | Image shown before video plays         |
| `autoplay`    | boolean | Start playing automatically            |
| `controls`    | boolean | Show video controls                    |
| `loop`        | boolean | Loop video continuously                |
| `muted`       | boolean | Mute audio by default                  |
| `playsinline` | boolean | Play inline on mobile (vs fullscreen)  |
| `preload`     | string  | Preload behavior: none, metadata, auto |
| `width`       | string  | Video width                            |
| `height`      | string  | Video height                           |
| `id`          | string  | Unique identifier                      |
| `class`       | string  | CSS class names                        |

## Basic Usage

```
Video
  src: /videos/intro.mp4
  controls: true
  poster: /images/video-poster.jpg
```

## Video Formats

For best browser compatibility, provide multiple formats:

| Format      | MIME Type  | Support            |
| ----------- | ---------- | ------------------ |
| MP4 (H.264) | video/mp4  | Best compatibility |
| WebM        | video/webm | Modern browsers    |
| OGG         | video/ogg  | Older Firefox      |

MP4 is recommended as the primary format.

## Autoplay Requirements

Browsers restrict autoplay to prevent unwanted media:

### Autoplay with Sound

* Requires user interaction first
* Won't work on page load

### Autoplay Muted ✓

* Works on page load
* Must include `muted: true`

```
Video
  autoplay: true
  muted: true
  loop: true
```

## Background Video

Create video backgrounds:

1. Set Video properties:
   * `autoplay: true`
   * `muted: true`
   * `loop: true`
   * `playsinline: true`
2. Style the Video:
   * Position: absolute
   * Object-fit: cover
   * Width/Height: 100%
3. Place in a container:
   * Position: relative
   * Overflow: hidden

```
Box (position: relative, overflow: hidden)
├── Video (position: absolute, inset: 0, object-fit: cover)
│     autoplay, muted, loop
└── Box (position: relative, z-index: 1)
    └── Content overlaying video
```

## Preload Options

| Value      | Description                       |
| ---------- | --------------------------------- |
| `none`     | Don't preload anything            |
| `metadata` | Load dimensions and duration only |
| `auto`     | Browser decides what to preload   |

Use `metadata` for most cases to balance performance and user experience.

## Responsive Video

### Maintain Aspect Ratio

```
Box (aspect-ratio: 16/9)
└── Video (width: 100%, height: 100%, object-fit: cover)
```

### Different Sources for Breakpoints

For different video qualities at different screen sizes, you may need custom HTML using [HTML Embed](/university/core-components/html-embed).

## Poster Image

The `poster` attribute shows an image before the video plays:

* Use a relevant frame from the video
* Optimize the image for quick loading
* Match the video's aspect ratio

## Performance Tips

1. **Compress videos**: Use tools like HandBrake or FFmpeg
2. **Consider file size**: Videos should be as small as possible
3. **Use lazy loading**: Set preload to "none" for off-screen videos
4. **Provide poster**: Show placeholder while video loads
5. **Host on CDN**: Fast delivery from edge servers

## Accessibility

1. **Provide captions**: Add text tracks for hearing impaired
2. **Avoid autoplay audio**: Always mute autoplay videos
3. **Include controls**: Let users pause/play
4. **Don't autoplay with motion**: Can trigger vestibular issues

## When to Use

| Use Video            | Use YouTube/Vimeo |
| -------------------- | ----------------- |
| Self-hosted files    | Platform content  |
| Background videos    | SEO benefits      |
| Full control needed  | Free hosting      |
| Privacy requirements | Analytics needed  |
| Custom player UI     | Social features   |

## Related Components

* [YouTube](/university/core-components/youtube) - YouTube video embeds
* [Vimeo](/university/core-components/vimeo) - Vimeo video embeds
* [Vimeo Background Video](/university/core-components/vimeo-background-video) - Vimeo for backgrounds
* [Image](/university/core-components/image) - Static images


# Vimeo

Embed Vimeo videos in your Webstudio pages.

{% embed url="<https://www.youtube.com/watch?v=YzyyZ10fcu4>" %}

The Vimeo component for Webstudio allows you to embed Vimeo videos into your site, whether as a video player or a background video. This component comes with several different customizable properties, including the ability to edit the preview image, set your video to “Do Not Track” mode, or customize the colors of the Vimeo Player controls to match your brand.

***

## Benefits of Vimeo

We have picked Vimeo to be Webstudio’s first video component for the following reasons:

1. **Modify Existing Videos**: It is possible to modify, edit or update your video on Vimeo without losing your video’s stats. And the best part of it all— the updated video stays on the same URL!
2. **Data Ownership**: As a Vimeo user, you retain complete ownership of all your data and intellectual property. You own what you create on Vimeo, unlike platforms like YouTube that bind your content with non-exclusive rights to use it as they please.
3. **Higher Quality Video and Audio**: Vimeo offers a higher bitrate and better video quality for their uploads compared to similar platforms.
4. **GDPR Compliance**: With complete control over your personal data, you get to choose how your data is tracked and recorded.
5. **An Adless Platform**: Vimeo operates as an adless platform, which means that they don’t allow advertisements on videos or sell ad space.

***

## How to use the Vimeo component in Webstudio

You can find the Vimeo Component in the Components Panel in the Media Section. Here is how you can add and use the component on your site:

### Adding the Vimeo component to your canvas

<picture><source srcset="/files/GW27fUw54Kz1uYwqQkZZ" media="(prefers-color-scheme: dark)"><img src="https://330243581-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTep4EGj6hSC54ozHujyK%2Fuploads%2Fgit-blob-d83bd1cac27b29a5fd037b3f8d6a27521380d365%2Fcomponents-panel-light.png?alt=media" alt="Components panel with Vimeo in the expanded Media group"></picture>

1. You can find the Vimeo component in the “Components Panel” in the Media section.
2. Add the component by dragging and dropping it to the canvas or with a click to put it inside the selected instance.
3. Once you have the component on your canvas, you can head to Settings on the right to add your video URL and make other changes.

![Vimeo component properties in Settings](https://330243581-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTep4EGj6hSC54ozHujyK%2Fuploads%2Fgit-blob-8cf82aad8bd55f1cc8abd3579fe95bf23b9ee328%2Fvimeo-component-properties.avif?alt=media)

## Sub-components

The Vimeo component contains three child instances visible in the Navigator:

| Sub-component     | Description                                                                                                                                                                                               |
| ----------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Preview Image** | The thumbnail shown before playback. Replace it with your own image in Settings.                                                                                                                          |
| **Spinner**       | The loading indicator shown while the video buffers. Hide it via the **Show** toggle in Settings.                                                                                                         |
| **Play Button**   | Contains a Box and a Play Icon. Style the Box (background, border-radius, etc.), swap the icon SVG via the HTML Embed inside, or hide the entire button via the **Show** toggle for background video use. |

### Modifying the preview image

1. Inside the Vimeo Component, you will find three instances- the Preview Image, Spinner and Play Button. These instances define the primary look of the embedded video player.
2. You can add a preview image to your video by selecting the “Preview Image” instance and choosing an image source in the “Properties” section.

***

## How to customize the Vimeo instance's properties

The Vimeo component comes with several properties that you can use to customize your video player. These properties appear in the Settings panel to the right of the canvas when your Vimeo component is selected.

Here’s how you can use the following properties:

![Vimeo properties in the Settings panel](https://330243581-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTep4EGj6hSC54ozHujyK%2Fuploads%2Fgit-blob-be2b00ee3dda24b900a8ec8dc67376d28d38cfea%2Fvimeo-instance-properties.avif?alt=media)

* **Show Preview**\
  Toggle this property on to render the preview image from Vimeo rather than the static image in Webstudio. This property is turned off by default because rendering the Vimeo preview requires making an API call which can slow down the speed of your page.
* **AutoPlay**\
  If you enable the “AutoPlay” property, your video will start playing automatically when the player loads.
* **Background Mode**\
  When enabled, the “Background Mode” property will hide the Vimeo Player’s controls, loop the embedded video and play it automatically.
* **Loop**\
  When enabled, the video will automatically restart when it reaches the end.
* **Muted**\
  Start the video with audio muted. Required for autoplay to work in most browsers.
* **Quality**\
  Set to "Auto" to let the player adapt video quality based on the viewer's bandwidth, or choose a fixed quality like 720p or 1080p.
* **Do Not Track**\
  If you enable the "Do Not Track" property, the Vimeo Player will not be able to track session data, such as cookies. It is important to note that video statistics, such as the number of views, would also no longer be recorded.
* **Controls Color**\
  With the "Controls Color" property, you can customize the colors of the Vimeo Player's controls on your site to match your brand identity. This affects the pause button, progress bar, and other player controls.
* **Lazy Load**\
  When enabled, the video player loads only when it enters the viewport. This improves initial page load performance, especially on pages with multiple videos or when the video is below the fold.
* **Other Properties**\
  You can customize your Vimeo component further by adding [other properties](https://developer.vimeo.com/player/sdk/embed) that are not listed in the "Properties" section by default.

## Customizing the play button and spinner

The Vimeo component includes customizable child instances:

1. **Preview Image**: Replace Vimeo's default preview with your own image
2. **Spinner**: Style or hide the loading spinner using the "Show" toggle in Settings
3. **Play Button**: Contains a Box and Play Icon
   * Style the Box (background color, border-radius, etc.)
   * Replace the Play Icon's SVG code in the HTML Embed
   * Set icon width/height to 100% for flexible sizing
   * Change the Play Icon's color via typography color on its parent
   * Hide the Play Button using the "Show" toggle for background video use

## Hiding player elements

For background videos or custom player designs, you may want to hide default elements:

1. Select the **Spinner** in the Navigator
2. In Settings, toggle **Show** to off
3. Repeat for the **Play Button** if desired

This creates a clean video background without visible controls.

## Related

* [Vimeo Background Video](/university/core-components/vimeo-background-video) – Full background video setup
* [YouTube](/university/core-components/youtube) – YouTube video embeds
* [Video Animation](/university/core-components/video-animation) – Scroll-controlled video


# Vimeo Background Video

Learn how to use the Vimeo Component as a background video on your site.

{% embed url="<https://vimeo.com/844215259>" %}

## Here is how you can use the Vimeo Component as a background video on your site:

1. Start by adding a Box component (Main Container) to your site and set its position to “Relative.” ![Vimeo background image step 1](https://330243581-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTep4EGj6hSC54ozHujyK%2Fuploads%2Fgit-blob-9378c228c68a3b2686ad188a521f14f6d5af0c01%2Fvimeo-bg-video-step-1.avif?alt=media)
2. Add a Vimeo component inside the Main Container.\
   ![Vimeo background image step 2](https://330243581-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTep4EGj6hSC54ozHujyK%2Fuploads%2Fgit-blob-c056c3f9baac49128cc8e29a9cbef8e1eddbd505%2Fvimeo-bg-video-step-2.avif?alt=media)
3. After this, add another Box component (Content) inside the container and set its position to “Absolute.” This will stack the Content Box on top of the Vimeo instance. ![Vimeo background image step 3](https://330243581-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTep4EGj6hSC54ozHujyK%2Fuploads%2Fgit-blob-1d21fe7a3e3c226a4be442eef2835fb32dad5a93%2Fvimeo-bg-video-step-3.avif?alt=media)
4. Now you can add any other content, like headings and paragraphs, to your Content Box, and it will be displayed over the background video.\
   ![Vimeo background image step 4](https://330243581-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTep4EGj6hSC54ozHujyK%2Fuploads%2Fgit-blob-a37fd9ac37dade5fef1e3357bfa85908b6e7021b%2Fvimeo-bg-video-step-4.avif?alt=media)
5. Make sure to enable the “Background Mode” property for the Vimeo instance to ensure that your video starts automatically and all the video player controls are disabled. ![Vimeo background image step 5](https://330243581-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTep4EGj6hSC54ozHujyK%2Fuploads%2Fgit-blob-2304d05eb7534d37f2d890b7aaaeb7d570a0efad%2Fvimeo-bg-video-step-5.avif?alt=media)

And done! Now you have a background video on this section of your site.

## Here are some other changes you can make to optimize it further:

1. Enable the “Muted” and “Loop” properties for your Vimeo instance to have the background video play in a loop without any sound.\
   ![Vimeo background image optimization step 1](https://330243581-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTep4EGj6hSC54ozHujyK%2Fuploads%2Fgit-blob-688c956d2c5a21ac2e39deb9858d5d872fbca04c%2Fvimeo-bg-video-step-6.avif?alt=media)
2. You can also hide or remove the Spinner on your output video for a more polished result. To hide it, select the Spinner instance from your Vimeo instance, head over to the Settings section and toggle off the “Show” option:\
   ![Vimeo background image optimization step 2](https://330243581-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTep4EGj6hSC54ozHujyK%2Fuploads%2Fgit-blob-6e08f34f5b284124b5f5eb079f89d6c58a72ab92%2Fvimeo-bg-video-step-7.avif?alt=media)

## Related

* [Vimeo](/university/core-components/vimeo) – Standard Vimeo embeds
* [YouTube](/university/core-components/youtube) – YouTube video embeds
* [Image](/university/core-components/image) – Static background images


# YouTube

The YouTube component provides extensive options to embed YouTube videos and playlists on your site.

<picture><source srcset="/files/GW27fUw54Kz1uYwqQkZZ" media="(prefers-color-scheme: dark)"><img src="https://330243581-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTep4EGj6hSC54ozHujyK%2Fuploads%2Fgit-blob-d83bd1cac27b29a5fd037b3f8d6a27521380d365%2Fcomponents-panel-light.png?alt=media" alt="Components panel with YouTube in the expanded Media group"></picture>

While YouTube offers embed codes, the YouTube component enables a much better experience, which is why it’s the recommended way to add YouTube videos to your site.

**Benefits:**

* Paste a URL instead of code
* Responsive by default
* Faster loading with customizable preview images and lazy loading
* UI inputs for all the settings instead of code
* Customizable play icon and loading icon

## Related

* [Vimeo](/university/core-components/vimeo) – Vimeo video embeds
* [Video Animation](/university/core-components/video-animation) – Scroll-controlled video
* [Image](/university/core-components/image) – Custom preview images


# Collection

Use Collection to iterate over data and repeat the same structure but with different data for each iteration.

<figure><img src="https://330243581-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTep4EGj6hSC54ozHujyK%2Fuploads%2Fgit-blob-1b39c4bf3e012577ddfd39d82ffc3e615c3bbb12%2Fcollection-component.png?alt=media" alt="Collection component" width="317"><figcaption></figcaption></figure>

## Why Collections are needed

Let's say you are building a list of all your blog posts. Each blog post will have an image, a title, and a link.

You have 50 blog posts. Does this mean you need to duplicate your design 50 times manually? No.

Collections let you design something once, and it will repeat it for every item in the array and contain the data for the current iteration (e.g., the blog post title).

## What's an array or object?

Collection can iterate over both **arrays** (lists) and **objects** (key-value pairs).

### Arrays

An array is a list of data. In the case of blog posts, this might look like:

```javascript
[
  { title: "Hello world" },
  { title: "Lorem ipsum" },
  { title: "Webstudio rocks!" },
];
```

When iterating over an array, the Collection Item contains the current item's data.

### Objects

An object is a set of key-value pairs. For example:

```javascript
{
  home: { label: "Home", url: "/" },
  about: { label: "About", url: "/about" },
  contact: { label: "Contact", url: "/contact" }
}
```

When iterating over an object, the Collection Item contains both the **key** (e.g., "home") and the **value** (e.g., `{ label: "Home", url: "/" }`). You can access them via `Collection Item.key` and `Collection Item.value`.

{% hint style="warning" %}
When binding data to a Collection, **you must bind the array or object**, i.e., the data you want to iterate over. If you bind something else, you'll receive an error.
{% endhint %}

If you are binding external data, the array is nested somewhere within.

<figure><img src="https://330243581-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTep4EGj6hSC54ozHujyK%2Fuploads%2Fgit-blob-e4534e5f14e219c0cc72bae7edbd8c2a0edad181%2Fright-and-wrong-way-collections.png?alt=media&amp;token=f5ede0b3-3120-476b-818b-e95bd140d5e0" alt="Right and wrong ways to bind data to a collection"><figcaption><p>Example of where the array is at for an external service (will vary for each service)</p></figcaption></figure>

In the image, the data bound to the component is:

1. ❌ Not the array
2. ❌ The first item in the array (0), not the entire list
3. ✅ The array

It's unclear why each item is correct or incorrect by just looking at the image, so let's clarify.

**You'll know when you get to the array when the next items in the autocomplete are numbers.** The numbers represent each item in the list. Once you see the numbers, backspace, as you don't want to select one item; you want the list.

<figure><img src="https://330243581-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTep4EGj6hSC54ozHujyK%2Fuploads%2Fgit-blob-6c490db4c3bdd689070de8b8ecd4baf29d71f2fa%2Fcomponent-array.png?alt=media" alt="Autocomplete showing array items in Binding"><figcaption><p>The numbers indicate each item in the list/array</p></figcaption></figure>

## How to use Collection

Add the Collection component to the canvas and either manually enter data (less common) or [bind data](/university/foundations/expression-editor#binding) to it (more common).

**The Collection iterates over the array, so you must bind just the array portion of your variable to it. See** [**What's an array or object?**](#whats-an-array-or-object) **for more info.**

Optionally, rename the default Collection Item variable to something more semantic. If you are iterating over blog posts, name it "Blog Post."

Now, you can add components to the Collection, and the Collection will automatically duplicate it for the number of items in the array. If you have multiple components, wrap everything in an [Element](/university/core-components/element) component.

Next, [bind](/university/foundations/expression-editor#binding) the Collection Item (or whatever you named it) to the various components. You will see it output a different value depending on the iteration.

## Using Collections with Radix Components <a href="#using-collections-within-accordions" id="using-collections-within-accordions"></a>

When working with [Radix Components](/university/radix), you might want to dynamically generate items for various components such as accordions, tabs, or menus.

**You must provide the Value field with a unique value for each item.** This is commonly done by binding an ID or slug from the dynamic data to the field.

### Accordions

<figure><img src="https://330243581-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTep4EGj6hSC54ozHujyK%2Fuploads%2Fgit-blob-d52a738867f36c6737a9d6f4595e0fbcea8bc9b6%2Faccordion-collection.png?alt=media&amp;token=870f16c3-76e1-4209-b44d-60919509555d" alt="Accordion Item settings with a unique collection value bound to the Value property"><figcaption><p>The Item has a unique value from the dynamic data bound to the Value field</p></figcaption></figure>

### Tabs

On Tabs, you need to manually add the "value" property on both the Tab Trigger and Tab Content by going to Settings > Properties & Attributes > "+".

Tab Triggers and Tab Contents maintain their relationship by having the same value. For example, the Tab Trigger with the value "asdf" will make the Tab Content with the value "asdf" active.

<div><figure><img src="https://330243581-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTep4EGj6hSC54ozHujyK%2Fuploads%2Fgit-blob-6785b322631e4be45d3864c8e186bc02a3120106%2Ftab-trigger.png?alt=media" alt="Tab Trigger settings with a custom Value property"><figcaption><p>Tab Trigger with custom value</p></figcaption></figure> <figure><img src="https://330243581-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTep4EGj6hSC54ozHujyK%2Fuploads%2Fgit-blob-bc58f62be874c43cee0a2c51d6914a36d59e70d9%2Ftab-content.png?alt=media" alt="Tab Content settings with the corresponding custom Value property"><figcaption><p>Tab Content with custom value</p></figcaption></figure></div>

Also, you can change the default value on the Tabs instance by binding the first value in the collection to it.

## Related Videos

{% embed url="<https://www.youtube.com/watch?v=oAaGTsHt-aE>" %}

## Related

* [Content Embed](/university/core-components/content-embed) – Render rich text from CMS
* [Markdown Embed](/university/core-components/markdown-embed) – Render markdown content
* [Variables](/university/foundations/variables) – Dynamic data binding
* [CMS](/university/foundations/cms) – Content management integration


# Content Embed

Content Embed allows styling HTML, which can be provided via the Code property statically or loaded dynamically from any Resource, for example, from a CMS.

<div align="left"><figure><img src="https://330243581-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTep4EGj6hSC54ozHujyK%2Fuploads%2Fgit-blob-107ed2b761e1693752fea7160e826dbf2e3893cc%2Fcontent-embed-component.png?alt=media" alt="Content Embed Component"><figcaption></figcaption></figure></div>

## Why Content Embed is needed

When designing most components on the site, you click on them and style them. However, this can’t be the case for HTML code because the HTML component is all that can be clicked on, not any of the headers, paragraphs, or other tags within.

Content Embed enables applying styles to the various tags contained within HTML.

## How to use Content Embed

Content Embed is located in Components > Data.

### 1. Add HTML

Once added to the canvas, the right panel will show a Code field. You can either add HTML directly to it or, more commonly, bind HTML to it from a Resource.

<figure><img src="https://330243581-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTep4EGj6hSC54ozHujyK%2Fuploads%2Fgit-blob-3fd97103d02c2c2e8700c3c1ce5f65bb32871188%2Fcontent-embed-code.png?alt=media" alt="HTML bound to Content Embed component"><figcaption><p>CMS data bound to Content Embed Code</p></figcaption></figure>

### 2. Style

In the Navigator, Content Embed has various HTML tags nested. Expand Content Embed, and you’ll see tags such as Heading 1, Link, Image, and much more.

{% hint style="info" %}
Links inside Content Embed are represented by the **Rich Text Link** sub-component, which behaves like the [Link component](/university/core-components/link). Select it in the Navigator to style all links within the embedded HTML.
{% endhint %}

<figure><img src="https://330243581-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTep4EGj6hSC54ozHujyK%2Fuploads%2Fgit-blob-3758aba0005a19f82d722a393a28e53a0016f8c6%2Fcontent-embed-style.png?alt=media" alt="Content Embed H2 styled"><figcaption><p>Heading 2 selected and styled</p></figcaption></figure>

Styles applied to each of these tags will apply to all occurrences of that tag within the Content Embed. For example, if you apply a border on the Image tag, then all images contained within the HTML will have a border.

## Image handling

Webstudio does NOT optimize images contained in the markup.

On the other hand, images bound to the [Image Component](/university/core-components/image) are [optimized by default](/university/core-components/image#optimize).

The difference is in markup, we are not mapping every element to a Webstudio Component; rather, the element is served as-is with the exception of your custom styles applied to it.

## HTML Embed vs. Content Embed

While very similar, there is one distinct difference: Content Embed enables styling the contained HTML.

If there is no need to apply styles to the contained HTML, then use the [HTML Embed Component](/university/core-components/html-embed). However, if you need to style the contents (which is typically the case if fetching HTML from a CMS), then use Content Embed.

## Similar components

{% content-ref url="/pages/rHKKkqfgRmjo89BY2EvK" %}
[HTML Embed](/university/core-components/html-embed)
{% endcontent-ref %}

{% content-ref url="/pages/kuK1zd7s0VXAsx4PszQD" %}
[Markdown Embed](/university/core-components/markdown-embed)
{% endcontent-ref %}

## Related

* [Markdown Embed](/university/core-components/markdown-embed) – Render markdown content
* [Collection](/university/core-components/collection) – Loop through CMS data
* [HTML Embed](/university/core-components/html-embed) – Embed custom HTML


# Markdown Embed

Markdown Embed converts Markdown to HTML and enables styling it.

<div align="left"><figure><img src="https://330243581-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTep4EGj6hSC54ozHujyK%2Fuploads%2Fgit-blob-b3bfd102f6fe71aad609dd12462359292700747a%2Fmarkdown-embed.png?alt=media" alt="Markdown Embed Component" width="519"><figcaption></figcaption></figure></div>

## Why Markdown Embed is needed

Some APIs (or users) provide rich text in Markdown format, which can't be rendered in the web browser. Markdown Embed converts Markdown to HTML and enables applying styles to the various tags contained within HTML.

## How to use Markdown Embed

Markdown Embed is located in Components > Data.

### 1. Add Markdown

Once added to the canvas, the right panel will show a Code field. You can either add Markdown directly to it or, more commonly, bind Markdown to it from a Resource.

<figure><img src="https://330243581-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTep4EGj6hSC54ozHujyK%2Fuploads%2Fgit-blob-ab0f12bf1592c2aff00032a1d7b0bb98b63cc23c%2Fmarkdown-embed-code.webp?alt=media" alt="Markdown bound to Markdown Embed component"><figcaption><p>CMS data bound to Markdown Embed Code</p></figcaption></figure>

### 2. Style

In the Navigator, Markdown Embed has various HTML tags nested. Expand Markdown Embed, and you’ll see tags such as Heading 1, Link, Image, and much more.

<figure><img src="https://330243581-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTep4EGj6hSC54ozHujyK%2Fuploads%2Fgit-blob-5b6ad3b5d4da5477d554b7a52fdf5af99a8071ab%2Fmarkdown-embed-style.webp?alt=media" alt="Markdown Embed List styled"><figcaption><p>List selected and styled</p></figcaption></figure>

Styles applied to each of these tags will apply to all occurrences of that tag within the Markdown Embed. For example, if you apply a border on the Image tag, then all images contained within the HTML will have a border.

## GitHub-style alerts

Use GitHub-style alerts to call attention to notes, tips, important information, warnings, and cautions:

```markdown
> [!NOTE]
> Add helpful context here.

> [!WARNING]
> Explain what readers should be careful about.
```

Write the alert type in uppercase at the start of a blockquote. Markdown Embed supports `NOTE`, `TIP`, `IMPORTANT`, `WARNING`, and `CAUTION`. You can use links, lists, code, and other Markdown inside an alert.

Select **Alert** to style every alert, or a type-specific descendant such as **Warning alert** to style one alert type. Markdown Embed renders the same `data-state` values as the standalone [Alert component](/university/core-components/alert).

## Image handling

Webstudio optimizes images contained in Markdown with the same responsive image pipeline as the [Image component](/university/core-components/image#optimize). It generates the appropriate `srcset` and `sizes` attributes and lazy-loads images by default.

Data URL images are served as-is without optimization.

## Similar components

{% content-ref url="/pages/rHKKkqfgRmjo89BY2EvK" %}
[HTML Embed](/university/core-components/html-embed)
{% endcontent-ref %}

{% content-ref url="/pages/2pY5IGDefvOgBTofmVWY" %}
[Content Embed](/university/core-components/content-embed)
{% endcontent-ref %}

## Related

* [Content Embed](/university/core-components/content-embed) – Render rich text/HTML
* [Alert](/university/core-components/alert) – Add and style standalone callouts
* [HTML Embed](/university/core-components/html-embed) – Custom HTML code
* [Collection](/university/core-components/collection) – Loop through CMS data


# Content Block

Content Block designates regions on the page where pre-styled instances can be inserted in Content mode.

[Content *mode*](/university/foundations/modes#content) enables editing existing content only inside Content Blocks. Content outside a Content Block is read-only for editors. Content Blocks also let editors add *new* content.

Content Block enables adding new content — not just any content, but specifically inserting new instances predefined in Templates.

Designers can create a library of templates, from little cards to fully built sections, and editors can insert instances of these pre-styled templates and modify their content.

Next is a breakdown of Content Block by mode:

1. [Design mode](#content-block-in-design-mode) ⬇️
2. [Content mode](#content-block-in-content-mode) ⬇️

## Content Block in Design mode

Without an MDX source, add content directly inside the Content Block, alongside **Templates**. No Body wrapper is needed. **Templates** defines the reusable designs that editors can insert in Content mode. Adding a one-off content instance does not add it to the editor's insertion menu. Add a design to Templates only when editors should be able to insert more instances of it.

Templates are not required for ordinary Markdown content. Custom components referenced by JSX in MDX must match a template in that Content Block; being registered in the Builder is not enough.

Sometimes providing team members or clients the ability to edit existing content doesn’t help them accomplish everything they need.

Instead, they may want to add new content without asking you.

Content Block enables you to define regions on the site where editors can add instances of templates that you create.

Next is how to use it.

### Step 1: Add Content Block

Add the Content Block to the various regions you want editors to insert new content.

For example, you can add it to a place on the page where entirely new sections can be added, or you can add it within a section for them to add additional content to.

### Step 2: Add templates

Notice that the child of Content Block is Templates.

Drag/build the various instances you want to provide editors here.

A Content Block must have exactly one direct Templates container. A missing or second Templates container makes the block invalid. Connected MDX content does not resolve or publish until you restore that structure.

For example, your client wants to update the section under the hero with the latest promotion. Sometimes the promotion is for an event while other times it’s a product. You can create those two designs, add them to “Templates” within Content Block, and your client can insert instances of the desired template and edit its content.

{% hint style="info" %}
Editors don’t have access to the Style Panel, so be sure to provide fully designed templates.
{% endhint %}

Reusable top-level templates appear in Content mode like this. Structural and inline templates supplied for MDX styling, such as table cells and emphasis, do not appear as standalone insertion choices.

<div><figure><img src="https://330243581-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTep4EGj6hSC54ozHujyK%2Fuploads%2Fgit-blob-3b7f78602eb6a20d1fbf498b10c25e2549af5572%2Ftemplates-design-mode.png?alt=media&amp;token=1b3d4cee-8a5c-4fdf-a90b-e586b3aafd19" alt="Templates in Design mode"><figcaption><p>Templates in Design mode</p></figcaption></figure> <figure><img src="https://330243581-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTep4EGj6hSC54ozHujyK%2Fuploads%2Fgit-blob-98ce7fe13e32392bcb648532004d90ddb6568404%2Ftemplates-content-mode.png?alt=media&amp;token=0ec12f3d-535f-45dd-a7a0-a2ba9d89926c" alt="Template in Content mode"><figcaption><p>Template in Content mode</p></figcaption></figure></div>

Each time they insert a template, its copy appears alongside the existing content: directly inside an unconnected Content Block, or inside **MDX content** when a file is connected. The Templates container remains protected source material.

### Step 3: Add an initial setup (optional)

Optionally, add instances directly inside the Content Block. If the block is connected to MDX, its file content appears inside **MDX content** instead.

<figure><img src="https://330243581-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTep4EGj6hSC54ozHujyK%2Fuploads%2Fgit-blob-14d2455caf0548f4c9f2ad4238df4556171dc854%2Fstartingpoint-content-block.png?alt=media&amp;token=1afc241a-04ce-448f-82e6-359a0eca6c9a" alt="Initial Feature instances inside a Content Block Body outlet" width="357"><figcaption><p>The Feature instances are provided as a starting point</p></figcaption></figure>

Doing so will provide an initial setup for editors.

Editors can delete top-level content instances. In MDX-connected blocks, these are the children of **MDX content**, not the surrounding designed shell. They cannot delete the Templates container, templates, or nested instances independently.

### Store content in an MDX file

Connect a `.mdx` file when the Content Block's body should live in Assets instead of the project's regular instance data. The designed shell and Templates list remain in the project. Markdown `.md` files cannot be connected to a Content Block.

Publishing a site with connected MDX articles downloads and compiles each reachable article, which can increase publish time. A `.md` file with Markdown Embed avoids that compilation but supports source editing instead of visual body editing. See [Choose Markdown or MDX](/university/foundations/content-engine#choose-markdown-or-mdx) for the full comparison.

To insert a custom component such as Accordion into **MDX content**, first add its design to **Templates**, then insert it from the template picker. Inserting custom components directly from the Add panel into MDX content is blocked. Basic Markdown elements, images, and code blocks can still be inserted directly. Place designer-owned, one-off components outside **MDX content**; they remain in the project and are not saved to the file.

Moving, pasting, or duplicating content into an MDX region also checks the destination's Templates. Unsupported components are rejected before the tree changes, including components nested inside a pasted container.

Shared Slots cannot be used inside Content Block Templates. Duplicate the Slot content into a regular template instead so each inserted MDX instance owns an editable copy.

#### Prepare the Content Block

1. Add the Content Block and design its shell in Design mode.
2. After connecting a file, place the **MDX content** outlet where the article body should render.
3. Keep exactly one direct **Templates** container. Style the standard document elements already provided inside it.
4. Add any reusable custom content to **Templates**.
5. Give every custom top-level template a unique instance name. Use a JSX-compatible name such as `PromotionCard` when you want component-style JSX.

New Content Blocks include direct templates for every supported Markdown element, including headings, paragraphs, marks, links, images, quotes, lists, task controls, code, separators, and tables. They do not include a Body outlet. Connecting an MDX file adds the outlet automatically. Existing blocks with a Body outlet remain supported without changing their saved structure.

When a file is connected, the Navigator labels its Body outlet **MDX content** and shows an MDX icon, even when the region is empty. Content inside that region is saved to the file; instances outside it remain in the project. Use the region's **MDX content settings** menu and choose **Open MDX file** to edit the connected file directly.

#### Create and connect the file

1. Open **Assets**, select **Create text file**, and create a file ending in `.mdx`.
2. Select the Content Block in Design mode.
3. Under **Properties & attributes**, select **Connect .mdx file** for **Source**.
4. Select the file, or bind **Source** to an expression that returns an MDX Asset ID.
5. If the Content Block already has body content, review the warning and confirm the connection. The file replaces the existing Body children.

<figure><picture><source srcset="/files/D8vcD54rdWF6jRxnE40N" media="(prefers-color-scheme: dark)"><img src="https://330243581-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTep4EGj6hSC54ozHujyK%2Fuploads%2Fgit-blob-1079edddd7b1dd46333e052722c292e2b796dc35%2Fcreate-mdx-file-light.png?alt=media" alt="New text file dialog with article.mdx entered in the filename field"></picture><figcaption><p>Change the default filename so that it ends in <code>.mdx</code>.</p></figcaption></figure>

<figure><picture><source srcset="/files/DQg4oK2MmzIazW8yKZLr" media="(prefers-color-scheme: dark)"><img src="https://330243581-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTep4EGj6hSC54ozHujyK%2Fuploads%2Fgit-blob-f68cbb76d6f0f2bf41cf4b77c1888072727a2530%2Fconnect-content-block-mdx-light.png?alt=media" alt="Content Block settings with Connect .mdx file under the Source property"></picture><figcaption><p>Connect the file from the Content Block's Source property.</p></figcaption></figure>

Select the connected filename to choose another file. Select **Open** to edit the current file. In Content mode, editors can see the filename and open it, but only a designer can connect, switch, bind, or disconnect the source.

#### Edit the body in Content mode

Edit the connected body with the usual Content mode controls. You can change text and supported properties, insert templates, reorder content, and delete content. Canvas changes appear immediately and then save to the MDX file.

While editing connected content, **Undo** and **Redo** restore source edits, including edits to referenced author files, without undoing the loading of the Content Block. This history keeps the last 20 source edits in the current project session. Reloading clears the history. If the file has changed outside that history, Webstudio does not overwrite it.

Component properties in Content mode are limited to authored content, such as links, media sources and alternative text, form labels and placeholders, code, and date values. Layout, dimensions, visual themes, form wiring, and interaction settings remain available only in Design mode.

If the file changes after a canvas edit starts but before it is saved, reload the Content Block before continuing. Webstudio keeps the local canvas state until reload and does not silently merge or overwrite either version.

#### Write Markdown and MDX

An `.mdx` file supports standard Markdown and constrained JSX in one document.

Write headings, paragraphs, links, lists, tables, code, images, and other standard document content as Markdown. Each node uses the uniquely matching semantic template from the Content Block when one exists. Matching uses the element tag or adapted component type, not the template's editable label. If no standard template exists, Webstudio renders the normal semantic fallback. The fallback is still a normal element or component, so its applicable component and global styles continue to work; it simply has no Content Block template styles. If more than one standard template matches, Webstudio reports a warning and uses the fallback.

Use lowercase JSX when Markdown cannot express the required HTML or SVG element or attributes:

```mdx
<section aria-label="Launch details">
  <h2>Launch offer</h2>
</section>
```

Standard SVG elements use the same syntax:

```mdx
<svg viewBox="0 0 24 24">
  <path d="M0 0h24v24H0z" />
</svg>
```

Use a capitalized JSX name for a uniquely named custom template:

```mdx
# Product update

Regular document content stays Markdown and uses the matching standard templates.

<PromotionCard tone="featured">
  ## Launch offer
</PromotionCard>
```

The JSX name matches the stable **Name** of a unique top-level template in the Content Block's Templates list. **Name** is a JavaScript identifier and is separate from the optional **Label** shown in the canvas. If no template has that name, the custom component is unresolved and is not rendered. Its JSX remains in the file so adding the matching template can resolve it later. A new template gets its default name from its root component or HTML tag, and duplicate defaults get deterministic numeric suffixes.

Use GitHub-style alert syntax for notes, tips, important information, warnings, and cautions:

```mdx
> [!NOTE]
> Add helpful context here.

> [!WARNING]
> Explain what readers should be careful about.
```

Write `NOTE`, `TIP`, `IMPORTANT`, `WARNING`, or `CAUTION` in uppercase at the start of a blockquote. Each alert becomes an [Alert component](/university/core-components/alert) in the Content Block. When Webstudio writes the file after a canvas edit, it uses the equivalent `<Alert variant="...">` JSX.

In the MDX editor, type `<` to autocomplete templates connected to the file and built-in Image and CodeText components. Inside a JSX tag, autocomplete suggests its supported properties and available property values.

Missing template references are marked as errors at their JSX source range. Hover the underline to read the explanation. For dynamic sources, the editor also uses currently rendered Content Blocks to find the file's templates.

Malformed MDX stays as an unsaved draft in the file editor. Fix the reported error before closing, or choose **Discard changes** to keep the last saved version. The saved article remains available while you repair the draft.

The Navigator shows an unresolved reference as **Missing template: Name**. Delete that item to remove its JSX, including its children, from the MDX file. Leaving the item in place preserves its source until the template is available.

When two component libraries export the same name, the core component keeps the plain identifier and the namespaced component gets a stable library prefix, such as `Checkbox` and `RadixCheckbox`. Component discovery reports the exact JSX identifier to use.

JSX attributes accept quoted static values and bare booleans. Webstudio converts quoted values to the property's declared string, number, or boolean type when possible. Imports, exports, expressions such as `{false}`, spreads, functions, and executable JavaScript are not supported. Internal forms such as `<ws.element>`, `ws:name`, `ws:tag`, `ws:label`, and `<$.*>` are not current authoring syntax.

An explicit JSX child tree that matches the designed template structure overlays its text and supported properties onto the cloned template descendants. Their template styles and structure stay intact. If the child structure does not match, the explicit children replace the template root's default children and each authored child resolves through a matching template when possible. An empty pair such as `<PromotionCard></PromotionCard>` clears the defaults, while a self-closing reference such as `<PromotionCard />` keeps them. If you edit inherited default content in Builder, Webstudio writes that content as explicit JSX children so the edit persists without changing the template.

Template resolution is live. If the file already contains `<PromotionCard />` or a Markdown element that has no matching template, adding the template later updates the rendered content without rewriting the MDX file. This also applies to elements added to an explicit JSX child tree. Renaming or removing a template re-resolves the same source.

If a Content Block that already renders connected MDX temporarily has zero or multiple Templates containers, Builder keeps the last valid rendered content and reports the structural error. Fixing the container structure resolves the current MDX again automatically.

Missing or duplicate custom templates show a source-ranged warning and a selectable placeholder in Builder. Published pages omit only the unresolved custom subtree. Invalid or unsupported MDX remains editable; Builder reports the source location and renders the valid content it can recover.

Before publishing, the Publish panel warns when a missing or ambiguous custom template would omit content. It lists the affected files and template names. The server checks the prepared publication content, including discovered dynamic article sources and nested Content Blocks; it is not limited to the open page. This reuses the publication bundle loading and content scan, then resolves its template dependencies again to collect warnings. The warning does not block publishing. Repair the listed templates to include that content.

Legacy files that already contain internal `ws.element` or `ws:name` syntax remain readable during migration, but Webstudio does not emit or recommend those forms. Component namespaces such as `$.*`, `radix.*`, and `animation.*` are unsupported; use the direct component identifier.

Keep custom template names stable after connecting MDX files. Webstudio prevents duplicate top-level template names. Renaming or deleting a referenced template warns that connected files will not be rewritten. If you continue, update the affected JSX references in the MDX files. An MCP-connected agent can preview and confirm that update across a selected group of files. A confirmed rename changes the reference name. A confirmed removal unwraps and preserves explicit authored children; a self-closing reference disappears because it has no authored children.

#### Use frontmatter in the designed shell

Edit frontmatter as YAML in the MDX file editor. Visual body edits preserve the frontmatter source, including comments and formatting.

Store every frontmatter image as an exact `$ref` object so it resolves to structured Asset metadata:

```yaml
featureImage:
  $ref: ./images/feature.png
```

To use frontmatter in the designed part of a Content Block, [bind](/university/foundations/variables) a property or text value to the Content Block's **document** variable. For example, bind a heading to `document.frontmatter.title`, an Image source to `document.frontmatter.featureImage.src`, and its alternative text to `document.frontmatter.featureImage.description`. This uses the image's Asset description instead of duplicating alternative text in frontmatter. Fields authored directly in the connected MDX file can be edited on the canvas and through supported Settings controls in Content mode; edits save back to that file. Direct bindings through a loaded Markdown or MDX `$ref` ending in `#frontmatter` save to the referenced file, with its write permissions enforced. Shared-record edits affect every document using that record. Computed expressions and JSON/body references remain read-only. Image replacement is supported: a direct Image source binding with `mode:"readwrite"` lets the picker replace the frontmatter `$ref`, while shared Asset metadata is edited in Asset settings.

**Check every article field**

An editable MDX body does not make the whole article editable. Check the title, excerpt, author details, dates, reading time, hero and inline images, alternative text, captions, links, and custom-component content in **Content mode**. Include header fields outside **MDX content**. Change each value through its UI control, check the saved file, reload, and restore the original value. Do not treat editing the raw MDX file as a substitute for an editor-facing control.

Replacing an article's image changes its frontmatter image `$ref`, not the shared Asset's resolved `.src`. With a writable direct Image source binding to `document.frontmatter.featureImage.src`, use **Choose source** in Content mode. The resolved URL itself remains read-only.

To edit shared alternative text, open **Choose source**, open the image's actions menu, then choose **Settings** and edit **Description**. A binding to `document.frontmatter.featureImage.description` uses that description. Changing it affects every use of the Asset; replacing an article's image does not change the original Asset's description.

**Keep formatted values editable**

Keep an editable value separate from its surrounding wording. For example, to display **— 5 min read**, place three inline text elements next to each other:

* Fixed text: `—` .
* A value element with its entire **Text Content** bound to `document.frontmatter.readingTime`.
* Fixed text: `min read`.

Keep the spaces in the fixed text. Do not combine the value and wording into one expression: concatenation, template literals, fallbacks, and formatting calls produce read-only values. Keep the bound value as the only text content of its element. The editor can then change the reading time without changing the fixed wording. For dates, bind a Date Time component's date property directly and use its formatting settings instead of formatting the binding expression.

#### Use one file in repeated or shared content

For a Content Block inside a Collection, bind **Source** to the current item's MDX Asset ID. Each Collection item then opens and edits its own file.

You can also connect multiple Content Blocks to the same MDX file. Editing the file or any connected occurrence updates every other occurrence.

When you copy a connected Content Block, page, template, or folder to another project, Webstudio copies the MDX file and the local files it references. The pasted Content Block points to those imported copies. If Webstudio cannot parse the MDX, it preserves the source file and reports that its dependencies could not be collected.

To create another post in a collection folder, use **New entry**. For an ordinary MDX Asset outside a collection, duplicate the file and edit the duplicate. Connect or bind the appropriate Content Block occurrence to the new file.

#### Disconnect the file

In Design mode, select the **Source** property label and choose **Reset value**. Disconnecting leaves the MDX file unchanged, removes its body from the canvas, and keeps the designed shell and Templates list with an empty Body outlet. If the source is bound inside a Collection, review the warning before confirming: resetting the shared Source property disconnects every Collection occurrence.

## Content Block in Content mode

### Verify the editor experience before handoff

Use this checklist when creating, migrating, or changing any Content Block, with or without an MDX source:

1. List every value the editor must change, its UI control, and where it saves. Include headers, dates, reading time, images, alternative text, links, and custom-component content—not just body text.
2. Use the appropriate control: a calendar for dates and an image picker for image replacement. Keep fixed punctuation outside directly bound values. Preserve existing field types and wording; a stored `4 min read` string can stay one directly bound value.
3. Edit every listed value in **Content mode**. Check its saved project data, MDX, or referenced file, reload, then restore and verify the original value.
4. Record each field as passed, failed, or not tested. A correct preview, successful API edit, or one editable heading does not verify other fields. Missing controls and read-only required fields mean the setup is unfinished.

Templates and designer-owned layout stay protected. This protection must not be used to exclude content the editor was promised they could change.

In [Content mode](/university/foundations/modes#content), you can edit existing content inside Content Blocks. But what if you want to add *new* content?

You can within Content Block — region(s) on the page the designer designates as a place you can add new content from building blocks to entire sections.

For example, on your homepage, you change out promotions. Sometimes they are events, and other times they are products. The designer can add the Content Block to that section on your homepage and provide you with an “Events template” and “Products template”. You can then insert instances of each template, delete them, and change out their content. The design is fully provided for you.

Next is how to use it.

### Step 1: Locate the region you want to change

On the left-hand side, there is the navigator showing you the various Content Blocks on the page.

<picture><source srcset="/files/yeGIGGM7D7MGV6APi1w9" media="(prefers-color-scheme: dark)"><img src="https://330243581-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTep4EGj6hSC54ozHujyK%2Fuploads%2Fgit-blob-403033b09aebcf0247bb84415d4c16b0c0f98c87%2Fcontent-blocks-navigator-light.png?alt=media" alt="Navigator with an expanded Content Block showing Templates and Content Block Body with paragraph and list children"></picture>

You can click on them to navigate to that part of the page.

### Step 2: Add template instances

Each Content Block can have a unique set of templates you can choose from.

On the canvas, hover where you want to insert, and the blue + button will appear. Click that, and you’ll see a list of templates provided by the designer.

<figure><img src="https://330243581-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTep4EGj6hSC54ozHujyK%2Fuploads%2Fgit-blob-98ce7fe13e32392bcb648532004d90ddb6568404%2Ftemplates-content-mode.png?alt=media&amp;token=0ec12f3d-535f-45dd-a7a0-a2ba9d89926c" alt="Template picker in Content mode"><figcaption><p>Templates the designer provided</p></figcaption></figure>

Select the one you want, and it’ll insert an instance/copy of that template.

Click into it to make changes. See more about editing content in [Content mode](/university/foundations/modes#for-editors).

### Step 3: Delete instances

You can delete a direct child of the Content Block in one of two ways:

1. The blue + button will turn into a red delete button if you hold the option/alt key on your keyboard.
2. Select the instance in the navigator, and press delete/backspace on your keyboard.

   <figure><img src="https://330243581-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTep4EGj6hSC54ozHujyK%2Fuploads%2Fgit-blob-1601c8047c510b16eddaebb80435ef04c235db7e%2Fdelete-instance-content-mode.gif?alt=media&amp;token=0584100b-19e2-461e-870f-c6350c7fdcc4" alt="Content mode add button changing to delete while Option or Alt is held"><figcaption><p>Hold Option or Alt</p></figcaption></figure>

{% hint style="success" %}
You can’t delete the template itself, so you can always add it back.
{% endhint %}

Beyond adding new content, you can edit the existing content inside the Content Block. See [Content mode](/university/foundations/modes#content) for more information.

## Related

* [Content Engine](/university/foundations/content-engine) – Build file-based sites with visually editable MDX articles
* [Slot](/university/core-components/slot) – Reusable component slots
* [Alert](/university/core-components/alert) – Highlight notes, warnings, and other callouts
* [Modes](/university/foundations/modes) – Builder modes including Content mode
* [Collection](/university/core-components/collection) – Iterate over dynamic data


# List

The List Component is used to create lists that structure and organize content within a webpage. You can create a list in your Webstudio project with the “List” and “List Item” Components.

> See [MDN: \<ul>](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/ul), [MDN: \<ol>](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/ol)

{% embed url="<https://www.youtube.com/watch?v=oKmMCOVGQOM&t=20s>" %}

## Components

### List

The outer container — renders as `<ul>` (unordered) or `<ol>` (ordered) depending on the Ordered property.

### List Item

The individual item inside a List — renders as `<li>`. Every List starts with three List Item instances. Add more from **Components > General** or duplicate an existing one with **Cmd+D** / **Ctrl+D**.

List Items can contain any component — text, images, links, buttons — making them suitable for rich, interactive lists beyond simple bullet points.

### How to use the List Component

The “List” component can be found in **Components > General**, and you can place it on your canvas by dragging and dropping it or clicking it in the Components panel. Every List component comes with three “List Item” instances as bullet points by default.

To add more list items, place a “List Item” component in your list from **Components > General**.

#### Duplicating List Items

You can quickly duplicate list items by selecting an item and pressing **Cmd+D** (Mac) or **Ctrl+D** (Windows).

#### Customizing Individual List Items

You can style individual list items differently from others by selecting a specific list item and applying styles or a design token to just that item. This is useful for highlighting specific items in your list.

#### Adding Content to List Items

List items can contain more than just text. You can drag other components like images, links, or buttons inside a list item to create rich, interactive lists. Once the List Component is on your canvas, you can customize its appearance in the Style panel. For instance, you can change bullet point styles, numbering formats, spacing, and text properties.

#### List Style Type

You can customize the “List Style” for your List instance by going to “List Style Type” in the Style panel. There are several options, including Disc, Circle, Square and Decimal.

Setting the List Style Type allows you to define the appearance of the bullet points or numbering for list items, depending on the content’s context and your design choices. For example, you might choose “Decimal” for an ordered list of steps, but for a more decorative list, you could pick Disc or Circle.

***

### How to customize a List instance's properties in Webstudio

<figure><img src="https://330243581-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTep4EGj6hSC54ozHujyK%2Fuploads%2Fgit-blob-af14ba5ee532c8386be0415a2e2a6e5047754bfe%2Flist-component-properties.avif?alt=media" alt="List settings showing ordered and unordered list properties"><figcaption></figcaption></figure>

You can customize a List’s properties by selecting it and going to Settings. Lists come with three base properties: Ordered, Reserved, and Start. To add additional properties, click the “+” next to Properties and add a new property.

#### Ordered

If you enable the Ordered property on your list, it will convert all list items to numbers and create an ordered list.

Ordered lists are commonly used for procedures, step-by-step guides, or any content that requires a defined sequence.

#### Start

The Start property sets the initial numbering value for ordered lists. It allows you to begin the list at a specific number, which is useful for scenarios where numbering should start from a custom value.

To use the Start property on your list, enable the ‘Ordered’ property first if your list items are in bullet points.

#### Reversed

The Reversed property reverses the order in which list items are numbered. This is effective for countdowns, rankings, or any content that benefits from a reverse order.

To use the Reversed property on your list, enable the ‘Ordered’ property first if your list items are in bullet points.

## Related

* [Collection](/university/core-components/collection) – Dynamic lists from data
* [Paragraph](/university/core-components/paragraph) – Text paragraphs
* [Separator](/university/core-components/separator) – Divide content sections


# HTML Embed

Embed custom HTML, CSS, and JavaScript in Webstudio pages.

{% embed url="<https://www.youtube.com/watch?v=ay0_plImm6w>" %}

The HTML Embed component enables the direct integration of custom HTML, CSS, and JavaScript code into your Webstudio project. With this component, you can extend the capabilities of your website, create interactive widgets, integrate external APIs, and implement personalized interactions.

***

## How to use the HTML Embed component

The "HTML Embed" component can be found in Components > General, and you can place it on your canvas by dragging and dropping it or clicking it in the Components panel.

***

## How to add custom code to an HTML Embed instance

You can add your custom HTML, CSS, or JavaScript code to the HTML Embed instance by selecting it and accessing its “Settings”.

The **Code** property is also available in Content mode when the HTML Embed is inside an editable Content Block.

### Code

The "Code" section has a text area for adding your custom code. After you paste your code here, it will be rendered on the canvas. You can add anything to your site including custom widgets, third-party services, API integrations, animations, and interactive content.

### Use assets from your project

When an HTML Embed contains a root-relative URL that matches a unique asset path in your project, Webstudio replaces it with that asset's current URL when rendering the embed. This works for `src` on `<audio>`, `<embed>`, `<img>`, `<input>`, `<script>`, `<source>`, `<track>`, and `<video>`; `srcset` on `<img>` and `<source>`; `href` on `<link>`; `data` on `<object>`; and `poster` on `<video>`. If more than one asset has the same path, Webstudio leaves that URL unchanged.

This applies to those HTML attributes only. It does not rewrite URLs inside script content, such as `fetch("/data.json")`, or CSS, such as `url("/image.png")`.

## Run script on canvas

The "Run Script on Canvas" toggle lets you execute scripts in the canvas preview. It helps you check how an embed behaves, but the preview is not guaranteed to match the published page exactly.

## Client only

If your HTML embed is script-free and only includes items like SVG icons, CSS, or static HTML, you don't need this option.

For embeds with scripts that change HTML, like GSAP animations or sliders, activate the "Client Only" option. This prevents server-side rendering of the HTML. The embed, along with scripts, will render correctly once client-side JavaScript is loaded.

Check out how to embed [GSAP animations](/university/how-tos/how-to-add-a-gsap-animation).

<figure><img src="https://330243581-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTep4EGj6hSC54ozHujyK%2Fuploads%2Fgit-blob-981d6c48deedcd3a58a80830fab871117e8c043a%2Fgsap.png?alt=media&amp;token=5678dff6-3b96-45b9-a659-d4d869321d99" alt="GSAP script configured in an HTML Embed"><figcaption></figcaption></figure>

***

## How to reuse your custom code across multiple web pages

You can reuse your custom code across your project by putting it inside a Slot instance.

1. Add an HTML Embed component to your canvas and set it up with custom code of your choice.
2. Add a [Slot](/university/core-components/slot) component to your canvas and place your HTML Embed instance inside it.
3. Now, you can copy and paste this slot instance anywhere on your Webstudio project.

Please note that any updates you make inside your slot will update all other instances of that slot.

## Avoid creating global variables

When using the tag, every variable you create is global by default, which can lead to unpredictable effects.\
\
Example (bad):

```html
<script>
  const a = 1;
</script>
```

In this example, we created a constant `a` and assigned the value 1 to it. However, because script tags by default use global scope, what really happened is that you created `window.a = 1`.\
\
What could go wrong? 😅

1. If you navigate away and come back to the page, the script gets executed again and you will get a syntax error: "Identifier 'a' has already been declared". This is due to the fact that const can be declared only once. You could workaround this by using `var` or `let` instead.
2. If your logic relies on an existing variable and you don't redefine it, it will use any random value that happens to be there at a given time.

### Solution 1 – module

When using the module type, the script has its own scope, and the variables don't become global.

```html
<script type="module">
  const a = 1;
</script>
```

### Solution 2 – function scope

Use an Immediately Invoked Function Expression (IIFE) to create a function scope.

```html
<script>
  (() => {
    const a = 1;
  })();
</script>
```

## Don't use DOMContentLoaded

The **DOMContentLoaded** event won't fire during client-side navigation between pages because, technically, the page isn't reloaded. Use the same logic you intended to write inside that event handler, and if you need to wait for an element to be rendered first, place the embed after it.

## Related

* [Head Slot](/university/core-components/head-slot) – Add scripts/styles to document head
* [Content Embed](/university/core-components/content-embed) – Render rich text from CMS
* [Element](/university/core-components/element) – Semantic HTML elements


# Animation Group

The foundation of all animations in Webstudio.

Animation Groups serve as containers that define how their contents animate. You can nest Animation Groups to create complex animations.

{% hint style="info" %}
Animation Group is one component of the animation engine. See [Animations](/university/foundations/animations) for an overview.
{% endhint %}

## Settings

### Run on canvas

By default, animations only run on the canvas when the Animation Group is selected in the navigator. Enable this setting to preview animations while designing other elements.

### Type

Choose between two animation trigger types:

* **View-based** – Triggers when an element enters or exits the viewport. Ideal for entrance and exit animations that respond to element visibility.
* **Scroll-based** – Progresses based on scroll position, perfect for scroll indicators.

### Axis

Defines which scroll direction controls the animation:

* **Y-axis** – Vertical scrolling (default).
* **X-axis** – Horizontal scrolling.

### Scroll source (for scroll-based animations)

Select which scrollable container drives the animation:

* **Nearest** – Uses the closest scrolling container affecting the element.
* **Root** – Uses the main document scroll.
* **Closest** – Uses the nearest ancestor element with scrolling enabled.

### Subject (for view-based animations)

Defines which element's visibility determines the animation progress, allowing animations to be triggered by different elements entering or exiting the viewport.

### Inset

Fine-tune animation start and end points relative to the viewport:

* **Top Inset** – Adjusts when the animation begins.
* **Bottom Inset** – Adjusts when the animation ends.

Positive values delay the animation, while negative values trigger it earlier.

{% hint style="info" %}
For an alternative workflow, the Style Panel accepts `view-timeline-inset` in Advanced on the child instances of the Animation Group. The usage of this property is automatically polyfilled for cross-browser support. If you opt to use the Style Panel, be sure to explicitly set the Top and Bottom Inset values to `auto` in the Animation Group.
{% endhint %}

### Animations (breakpoint control)

Each animation in the Animation Group can be enabled or disabled per breakpoint. This is useful for:

* Disabling complex animations on mobile for performance
* Creating different animation experiences across screen sizes
* Showing simpler animations on smaller screens while keeping rich effects on desktop

To control animation visibility at a breakpoint:

1. Select a breakpoint in the canvas
2. Hover over an animation in the list
3. Click the eye icon to toggle visibility

When disabled at a breakpoint, the animation won't run, but the element still displays in its final "in" state (as designed on the canvas).

<figure><img src="https://330243581-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTep4EGj6hSC54ozHujyK%2Fuploads%2Fgit-blob-3e72a7bdbd598438115b8484be5abe0d1b5ae0d4%2Fanimation-group-disable-breakpoint.png?alt=media" alt="Disable animation at breakpoint"><figcaption><p>Hover over an animation to reveal the visibility toggle for the current breakpoint</p></figcaption></figure>

### Debug mode

{% hint style="warning" %}
Debug mode is experimental. While it can be very helpful in fine-tuning animations, there are some known issues with it—especially in complex animations.
{% endhint %}

Enables visualization tools to fine-tune animation timing and progression. When enabled, a small overlay appears displaying animation state details:

* Current status (idle, running).
* Progress percentage.
* Timeline position.

This debugging information is visible only in design mode and does not affect the live site.

## Define your animation

Once your Animation Group settings are configured, define the animation behavior. Webstudio offers preset animations for quick setup while allowing custom animations.

### Animation presets

The available presets depend on the animation type:

#### **View-based animation presets**

* **Fade In** – Smoothly transitions an element from invisible to visible.
* **Fade Out** – Gradually fades an element as it exits the viewport.
* **Fly In** – Moves an element into position upon entry.
* **Fly Out** – Animates an element away upon exit.
* **Wipe In** – Creates a revealing effect.
* **Wipe Out** – Gradually hides content.
* **Parallax In** – Creates depth by moving elements at different speeds during entry.
* **Parallax Out** – Applies a parallax effect as elements exit.

#### **Scroll-based animation presets**

* **Fade In** – Gradually reveals content based on scroll position.
* **Fade Out** – Progressively hides content as the user scrolls.

### Animation properties

Each animation can be customized using the following properties:

#### **Timing**

* **Range Start** and **Range End** – Define when the animation starts and stops, typically based on element position or scroll progress. See [Scroll-driven Animations](https://scroll-driven-animations.style/tools/view-timeline/ranges) for an interactive tool that explains the various options.
  * **Entry** – Animates during the subject element entry (starts entering → fully visible)
  * **Exit** – Animates during the subject element exit (starts exiting → fully hidden)
  * **Contain** – Animates only while the subject element is fully in view (fully visible after entering → starts exiting)
  * **Cover** – Animates entire time the subject element is visible (starts entering → ends after exiting)
  * **Entry Crossing** – Animates as the subject element enters (leading edge → trailing edge enters view)
  * **Exit Crossing** – Animates as the subject element exits (leading edge → trailing edge leaves view)
* **Duration** – Setting a duration will play the animation when it enters the scrollport (taking into account Range Start and Inset), then animate for the duration and end. On the published site, the animation will play just once, but in the builder, it will play multiple times to aid in building. Because the duration dictates when the animation will end, the Range End field will be disabled.
* **Fill Mode** – Controls how an element appears before and after the animation:
  * **None** – Only displays its animation styles *during* the animation.
  * **Forwards** – The animation transitions from the **canvas styles → animation styles**. Preferred for "*out*" animations.
  * **Backwards** – The animation transitions from the **animation styles → canvas styles**. Preferred for "*in*" animations. For example, the opacity is "1" by default on the canvas so to fade it in, you'd set "0" in the animation. It then transitions from the animation style (0) to the canvas style (1).
  * **Both** – Combines "Forwards" and "Backwards," transitioning smoothly before and after animation.
* **Easing** – Defines the speed curve of the animation:
  * **Linear** – Moves at a constant speed.
  * **Ease-In** – Starts slow, then accelerates.
  * **Ease-Out** – Starts fast, then decelerates.

#### **Keyframes**

* Define specific points in the animation timeline.
* Each keyframe can modify multiple CSS properties.
* Offset values determine when changes occur in the keyframe timeline. Use `0` for the start of the timeline and `1` for the end.

You can stack multiple animations on the same element by adding additional animations to your Animation Group. This enables complex, multi-step effects.

## Helper animation components

Animation Group is the controller for all animation helper components. Put regular instances directly inside an Animation Group when you want the group to animate those instances. Put these helper components directly inside an Animation Group when you need specialized behavior:

* **Text Animation** – Splits descendant text into characters, words, or custom separators and applies the parent Animation Group progress to each part.
* **Stagger Animation** – Applies the parent Animation Group progress across its direct child elements in sequence.
* **Video Animation** – Passes the parent Animation Group progress and visibility state to a Video child.

Text Animation, Stagger Animation, and Video Animation should be direct children of Animation Group because they consume the group’s progress. The actual animated CSS properties still belong in the Animation Group keyframes.

## Structure

Animation Group should wrap the content it controls. The final readable or visible state belongs in the normal Style Panel styles on the animated instances. The Animation Group keyframes define the starting state for "in" animations or the ending state for "out" animations.

Use this same structure for helper components too: Animation Group stays the direct parent, and the helper component receives the group progress.

## CSS input fields

The input fields support various units (`px`, `%`, `vh`, `dvh`, `lvh`, etc.), CSS functions (`calc()`, `clamp()`, etc.), or `"auto"` for default values, and [CSS variables](/university/foundations/css-variables).

Additionally, two special CSS variables are automatically exposed:

* `--index`: Represents the current child's position in the Animation Group sequence (0, 1, 2, etc.).
* `--total`: Represents the total number of children in the Animation Group.

These variables are useful for advanced animation patterns, such as:

* Creating unique rotations for each child element using `calc(var(--index) * 45deg)`.
* Applying varying translations with `calc(var(--index) * 100px)`.
* Adjusting animations relative to the total number of elements, like `calc(100% / var(--total))` for evenly distributed effects.

You can use these variables anywhere in descendant elements or pass them as parameters to nested animations, enabling complex, coordinated motion effects.

## Mental model and design patterns

Thus far, we’ve covered the building blocks of animations. However, there are various strategies to consider when composing animations.

### Mental model

Understanding how to approach animation composition can simplify the process, making it easier to comprehend and reducing mental fatigue during development.

When animating instances *in* (e.g., fading in or performing complex transitions like translating and rotating scattered items into a neat grid), **the mental model is to set the styles in the Style Panel to their&#x20;*****final "in" state*****&#x20;and define the styles in the Animation Group as their&#x20;*****beginning state***. For animations transitioning from one value to another (e.g., opacity from 0 to 1), the Animation Group requires only a single keyframe.

Here are examples of animations with their corresponding styles in the Style Panel and Animation Group:

{% hint style="success" %}
Animations transitioning from a custom style to their default state don’t require the default state to be explicitly defined in the Style Panel. For instance, to fade something in (i.e., opacity `0` → `1`), you only need to specify the non-default value (`opacity: 0`) in the Animation Group. CSS will interpolate to the default state (e.g., `opacity: 1`) automatically. However, you must define a style in the Style Panel if the final "in" state is not a default value (defaults being `0`, `none`, `auto`, etc.). For example, if an image’s final state is slightly rotated, such as `rotate: 3deg`, this must be explicitly set in the Style Panel, as the default rotation is effectively `0`.
{% endhint %}

| Animation       | Style Panel                                           | Animation Group                                       |
| --------------- | ----------------------------------------------------- | ----------------------------------------------------- |
| Fade in         | Nothing                                               | `opacity: 0`                                          |
| Rotate          | Nothing                                               | `rotate: 50deg`                                       |
| Translate Y     | Nothing                                               | `translate: 0 200px`                                  |
| Grow box shadow | Bigger box shadow: `box-shadow: 0 0 50px 100px black` | Smaller box shadow: `box-shadow: 0 0 25px 50px black` |

This mental model has two key implications:

1. **The canvas reflects the final "in" state.** This approach simplifies designing on the canvas, allowing you to see the site’s final appearance without relying on animations. Sometimes animations can interfere with design work, so being able to turn them off is valuable. You might also want to disable animations at specific breakpoints. Since the canvas is designed for the final "in" state, disabling animations won’t compromise the site’s look—everything still appears polished!
2. **For "out" animations, the opposite mental model applies.** Here, animations transition from the default state to the defined animation state. For example, to fade out, the Animation Group would set `opacity: 0`. This matches the value used for an "in" animation, but there’s a critical difference: **the fill mode must be set to `forwards`**. This ensures the animation moves from the default state to the animation state and stays there, while `backwards` would reverse the direction (from animation state to default). See "[Timing](#timing)" for more details.

### Design patterns

There are multiple ways to structure Animation Groups and their settings, each offering different levels of maintainability and complexity. The best approach often depends on the animation’s complexity.

#### Direct style animation pattern

In this pattern, you directly define animation properties, such as `opacity: 0`, within the Animation Group. This is the method discussed so far.

It works for both simple and complex animations, but maintainability can suffer as complexity grows. The Animation Group controls the styles of its *direct* children. If multiple levels of nested instances need animation, each level in the hierarchy requires its own Animation Group. As the number of animation groups increases, so does the management overhead and learning curve.

#### Custom property animation pattern

In this pattern, the Animation Group animates custom properties ([CSS variables](/university/foundations/css-variables)), such as `--child-rotate: 50deg`. These properties are both defined and applied in the Style Panel, unlocking features like UI controls and [Tokens](/university/foundations/design-tokens) for reusability.

Beyond accessing Style Panel capabilities, this pattern offers another major advantage: only a single Animation Group is needed for the entire composition. The Animation Group controls the *values* of the CSS variables, not *where* they’re applied. In contrast, the direct style pattern manages both values and their application (the group’s direct children).

## Related

* [Stagger Animation](/university/core-components/stagger-animation) – Stagger animations across multiple children
* [Text Animation](/university/core-components/text-animation) – Split text for character/word animations
* [Video Animation](/university/core-components/video-animation) – Control video playback with scroll
* [Animations](/university/foundations/animations) – Animation concepts and workflows


# Text Animation

The Text Animation component simplifies animating text by automatically splitting it into individual words or characters, allowing for dynamic effects without manual wrapping.

Let's say you want to animate one letter at a time - instead of having to manually wrap *each* letter in an [Animation Group](/university/core-components/animation-group), you can simply wrap a text instance (like a heading) in the Text Animation component.

{% hint style="info" %}
Text Animation is one component of the animation engine. See [Animations](/university/foundations/animations) for an overview.
{% endhint %}

The Text Animation component handles splitting the text under the hood, making it easy to prepare text for animation.

{% hint style="info" %}
Just remember that you'll need to wrap the Text Animation component in an [Animation Group](/university/core-components/animation-group) to define and control the actual animation behavior. The Animation Group must be the **direct** **parent** of the Text Animation.
{% endhint %}

You can split text by:

* Spaces (enabling you to animate each word)
* Characters (enabling you to animate each letter)
* `#`
* `~`

## Settings

### Split By

Defines how text is split into animated parts.

* **Characters** (`char`) – Animates one character at a time. This is the default.
* **Spaces** (`space`) – Animates one word at a time.
* **`#`** (`symbol "#"`) – Splits text at the `#` symbol.
* **`~`** (`symbol "~"`) – Splits text at the `~` symbol.

### Sliding Window

Controls how many text parts animate concurrently. The default is `5`.

* **`0`** – Creates an instant typewriter-like step between parts.
* **`(0..1]`** – Animates one text part at a time.
* **`> 1`** – Animates multiple text parts at once, creating an overlapping wave.

### Easing

Controls the easing within the sliding window. The default is `linear`. Supported values are `linear`, `easeIn`, `easeInCubic`, `easeInQuart`, `easeOut`, `easeOutCubic`, `easeOutQuart`, `ease`, `easeInOutCubic`, and `easeInOutQuart`.

## Structure

The Text Animation component must be the direct child of Animation Group. The text-containing element goes inside Text Animation. Configure word or character splitting on Text Animation, then define the actual opacity, translate, scale, or other animated styles on the parent Animation Group.

## Under the hood

Here's an example of what happens automatically under the hood.

If you wrap a heading component that outputs the following HTML:

```html
<h2>Hello World</h2>
```

in a Text Component and split the text by spaces, it will appear as:

```html
<h2>
  <span>Hello</span>
  <!-- The space is automatically ignored by the animation. -->
  <span> </span>
  <span>World</span>
</h2>
```

Now the parent Animation Group can effectively target each word individually.

## Usage notes

* Text Animation can contain Heading, Paragraph, Text, or other text-containing instances.
* The Text Animation component splits non-empty text nodes under the hood, including text inside nested instances.
* The parent Animation Group provides the actual animation styles, such as opacity, translate, scale, or rotate.
* Use the regular Style Panel to define the final readable state of the text. Use Animation Group keyframes to define the starting state for "in" animations or the ending state for "out" animations.

## Related

* [Animation Group](/university/core-components/animation-group) – Required parent for animations
* [Stagger Animation](/university/core-components/stagger-animation) – Stagger child animations
* [Heading](/university/core-components/heading) – Headings to animate
* [Paragraph](/university/core-components/paragraph) – Text to animate


# Video Animation

The Video Animation component renders a video and starts it based on Animation Group settings.

To play a video when it enters the scroll port, you can insert an Animation Group and then insert Video Animation inside it. Upload a short video directly to Webstudio and select it in the Video instance inside Video Animation. The video will start playing according to the Animation Group settings once the video reaches a certain position in the scroll port.

{% hint style="info" %}
Video Animation is one component of the animation engine. See [Animations](/university/foundations/animations) for an overview.
{% endhint %}

{% hint style="info" %}
Just remember that you'll need to wrap the Video Animation component in an Animation Group to define and control the actual animation behavior. The Animation Group must be the **direct** **parent** of the Video Animation.
{% endhint %}

## Structure

Video Animation expects a Video component inside it. When you insert the Video Animation template, Webstudio creates this structure for you:

```html
<AnimationGroup>
  <VideoAnimation>
    <Video />
  </VideoAnimation>
</AnimationGroup>
```

Configure the video source on the Video child.

## Settings

### Timeline

When Timeline is enabled, the Video child receives timeline progress from the parent Animation Group. Use this when you want video playback to follow scroll/view progress. When Timeline is disabled, the Video child still receives visibility/progress state from the group.

## Usage notes

* Use short videos for scroll-linked playback.
* Videos with frequent keyframes seek more smoothly when playback follows scroll progress.
* A common setup is a view-based Animation Group with a `cover 0%` to `cover 100%` range so the video responds while the element covers the scrollport.
* Keep the Video Animation component as the direct child of Animation Group; put the actual Video component inside Video Animation.

## Related

* [Animation Group](/university/core-components/animation-group) – Required parent for video animation
* [Vimeo](/university/core-components/vimeo) – Embed Vimeo videos
* [YouTube](/university/core-components/youtube) – Embed YouTube videos


# Stagger Animation

Creates a cascading effect by animating child elements sequentially.

Stagger Animation applies the parent animation to one child at a time, producing a smooth, sequential animation flow.

{% hint style="info" %}
Stagger Animation is one component of the animation engine. See [Animations](/university/foundations/animations) for an overview.
{% endhint %}

{% hint style="info" %}
Just remember that you'll need to wrap the Stagger Animation component in an [Animation Group](/university/core-components/animation-group) to define and control the actual animation behavior. The Animation Group must be the **direct** **parent** of the Stagger Animation.
{% endhint %}

## Sliding Window

Controls how many child elements animate concurrently during the stagger effect.

* **Default**: `1`
* **`0`**: Disables the transition animation for each element. Elements **appear instantly** one after the other in sequence.
* **`1`**: Classic stagger effect. Only **one** element animates (with its transition, e.g., fade-in) at any given time.
* **`> 1`**: Overlapping stagger effect. **Multiple** elements animate concurrently. The number specifies the maximum elements animating simultaneously (e.g., `3` means up to three animate at once), creating a smoother, wave-like reveal.

## Easing

Controls the easing within the sliding window. The default is `linear`. Supported values are `linear`, `easeIn`, `easeInCubic`, `easeInQuart`, `easeOut`, `easeOutCubic`, `easeOutQuart`, `ease`, `easeInOutCubic`, and `easeInOutQuart`.

## Structure

The Stagger Animation component must be the direct child of Animation Group. Put the repeated cards, list items, or rows directly inside Stagger Animation. Start with the default `slidingWindow` and `easing`; add overrides only when you intentionally want a different rhythm.

## Usage notes

* Stagger Animation applies the parent Animation Group progress to its **direct** children.
* Put the repeated elements, such as cards, list items, images, or text rows, directly inside Stagger Animation.
* Define the animated CSS properties on the parent Animation Group, such as opacity, translate, scale, or rotate.
* Use the regular Style Panel to define the final state of each child. Use Animation Group keyframes to define the starting state for "in" animations or the ending state for "out" animations.

## Related

* [Animation Group](/university/core-components/animation-group) – Parent container for animations
* [Text Animation](/university/core-components/text-animation) – Animate text by character/word
* [Collection](/university/core-components/collection) – Dynamic lists to animate


# Head Slot

Head Slot is a component that enables visually customizing the \<head> on a per-page basis. It’s useful for setting canonical URLs, alternate links, and more.

{% embed url="<https://www.youtube.com/watch?v=LeE52a_EWFw>" %}

<picture><source srcset="/files/GW27fUw54Kz1uYwqQkZZ" media="(prefers-color-scheme: dark)"><img src="https://330243581-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTep4EGj6hSC54ozHujyK%2Fuploads%2Fgit-blob-d83bd1cac27b29a5fd037b3f8d6a27521380d365%2Fcomponents-panel-light.png?alt=media" alt="Components panel with Head Slot in the expanded General group"></picture>

The Head Slot provides a visual interface for controlling your website's `<head>` section. Instead of writing code, you can manage meta tags, canonical URLs, and other head elements directly in the builder.

## Key information

* Even though the Head Slot is visually in the Body instance, the contents of Head Slot are added to the `<head>`. This hierarchy enables [data variables](/university/foundations/variables) defined on the Body to be used in the head.
* If Head Slot and Page Settings define the same data, such as meta title, Head Slot will take priority. Similarly, a default canonical is output on every page and references the current path. By specifying a canonical in Head Slot, the default canonical will not be displayed on the page.
* Head Slot comes with an instance for Title, Link, and Meta. These can be duplicated. If you remove them and later need to add them back, you can copy them from a new Head Slot instance.

## Supported components

Head Slot accepts these components:

* **Title** – Set the document title.
* **Link** – Add canonical, alternate, preload, preconnect, and other link relationships.
* **Meta** – Add metadata that is not covered by Page Settings.
* [**JSON-LD**](/university/core-components/json-ld) – Add structured data as an `<script type="application/ld+json">` element.

### Title

Title sets or overrides the document title shown in browser tabs, bookmarks, and search results. Prefer the title field in Page Settings for standard SEO; use Title in Head Slot when you need a custom or dynamic value.

### Meta

Meta creates a `<meta>` element. Use its `name` or `property` field to identify the metadata and `content` for its value. Common uses include metadata not available in Page Settings and Open Graph properties for social sharing.

### Link

Link creates a `<link>` element. Set `rel` to describe the relationship and `href` to identify the resource or URL. Common uses include canonical and alternate URLs, favicons, external stylesheets, preconnects, and preloads.

For standard titles, descriptions, social images, and search visibility, start with [Page Settings](/university/foundations/page-settings). Use Head Slot when you need finer control or values bound to dynamic data.

## Common Use Cases

### Canonical URLs

Prevent duplicate content issues by specifying the preferred URL:

1. Add a **Link** element inside Head Slot
2. Set `rel` to `canonical`
3. Set `href` to the canonical URL (can be bound to a variable for dynamic pages)

### Alternate Language Links

For multilingual sites, specify language alternatives:

1. Add a **Link** element
2. Set `rel` to `alternate`
3. Set `hreflang` to the language code (e.g., `es`, `fr`)
4. Set `href` to the URL of the translated page

### Custom Meta Tags

Add any meta tag not available in Page Settings:

1. Add a **Meta** element
2. Set the `name` or `property` attribute
3. Set the `content` attribute

### Preconnect and Preload

Improve performance by hinting resource loading:

1. Add a **Link** element
2. For preconnect: `rel="preconnect"` with `href` pointing to the domain
3. For preload: `rel="preload"` with appropriate `as` and `href` attributes

## Third Party Documentation

Many elements in the head are quite technical and require reading documentation for proper usage.

Here is a list of relevant docs:

* [Head (MDN)](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/head)
* [Canonical (Google)](https://developers.google.com/search/docs/crawling-indexing/canonicalization)
* [Meta (MDN)](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/meta)
* [Link (MDN)](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link)
* [Title (MDN)](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/title)
* [Rel (MDN)](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/rel)
* [Pagination (Google)](https://developers.google.com/search/docs/specialty/ecommerce/pagination-and-incremental-page-loading)

## Related

* [SEO settings](/university/foundations/seo-settings) – Page-level SEO configuration
* [Project settings](/university/foundations/project-settings) – Global project configuration
* [JSON-LD](/university/core-components/json-ld) – Add structured data to the document head
* [HTML Embed](/university/core-components/html-embed) – Custom HTML in body


# JSON-LD

Add structured data to a page with valid JSON-LD in the document head.

The JSON-LD component adds structured data to a page as a `<script type="application/ld+json">` element. Search engines can use this data to understand entities such as organizations, products, articles, events, and local businesses.

## Add JSON-LD to a page

1. Add a [Head Slot](/university/core-components/head-slot) to the page if it does not already have one.
2. Add the **JSON-LD** component inside the Head Slot.
3. Select the JSON-LD instance and enter a JSON object or array in **Code**.
4. Publish the site and test the page with a structured-data validation tool.

JSON-LD can be placed in the page body, but Head Slot is the recommended location. Content inside Head Slot is emitted in the document `<head>`.

## Code

Enter JSON only. Do not include a `<script>` element because the component creates it automatically.

For example, an organization can be described with:

```json
{
  "@context": "https://schema.org",
  "@type": "Organization",
  "name": "Northstar Studio",
  "url": "https://example.com",
  "logo": "https://example.com/logo.png"
}
```

The editor formats valid JSON when it opens and when it loses focus. It does not reformat while you are typing. The project stores the JSON without presentation-only whitespace.

The top-level value must be an object or an array. Most Schema.org documents should include `"@context": "https://schema.org"`.

## Dynamic structured data

The Code property can be bound to data for dynamic pages. The resulting value must still be a valid JSON object or array. Use dynamic JSON-LD for values such as a product name, price, availability, article author, or canonical URL.

Keep the structured data consistent with content visitors can see on the page. Do not describe content that is absent or misleading.

## Validate JSON-LD

Webstudio validates fixed JSON-LD values and can report:

* Invalid JSON that cannot be emitted as structured data.
* Invalid JSON-LD keyword values and value-object combinations.
* A missing top-level `@context`.
* Unknown or superseded Schema.org types and properties.
* Properties unsupported by the supplied Schema.org type.
* Primitive values that conflict with a property's Schema.org range.
* JSON-LD incorrectly entered as custom page metadata.

Dynamic values require testing on a rendered or published page because their final value is only known at runtime.

Schema.org vocabulary findings are warnings because JSON-LD can use custom vocabularies and extensions. Passing Webstudio validation does not guarantee eligibility for a search-engine rich result; search engines apply additional type-specific requirements and content policies.

You can also validate published pages with:

* [Rich Results Test](https://search.google.com/test/rich-results)
* [Schema Markup Validator](https://validator.schema.org/)

## JSON-LD and other components

Use JSON-LD instead of an [HTML Embed](/university/core-components/html-embed) when adding structured data. JSON-LD accepts only the data, creates the correct script element, and provides focused validation.

Do not add JSON-LD through custom metadata in Page Settings. Custom metadata creates `<meta>` elements and cannot create a JSON-LD script.

## Related

* [Head Slot](/university/core-components/head-slot) - Add page-specific elements to the document head
* [SEO settings](/university/foundations/seo-settings) - Configure page titles, descriptions, and indexing
* [Variables](/university/foundations/variables) - Bind components to dynamic data


# XML Node

The XML Node is used to create an XML document, such as a sitemap.

{% hint style="info" %}
Webstudio automatically generates a sitemap for static pages such as Home and About. If you are integrating with a CMS, you can use this component to create a sitemap for CMS data.
{% endhint %}

<figure><img src="https://330243581-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTep4EGj6hSC54ozHujyK%2Fuploads%2Fgit-blob-c01e838c4799f0aa78ec70abe0326a3305b4dd18%2Fxml-component.png?alt=media" alt="XML Node component available for an XML page"><figcaption><p>The component appears when Document type is XML</p></figcaption></figure>

## How to use the XML Node component

1. Go to Page Settings > Document Type and select XML from the dropdown

   <figure><img src="https://330243581-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTep4EGj6hSC54ozHujyK%2Fuploads%2Fgit-blob-7dc118c1dc6488075f3cc0ff3c5b4470b0c6695f%2Fpage-settings-xml-doc-type.png?alt=media" alt="Page Settings with Document type set to XML"><figcaption></figcaption></figure>
2. Go to Components > XML and add XML Node (this component won’t show until Step 1 is completed)
3. Set the tag and text content (e.g., “loc” and “<https://example.com>)

## Tips

* XML Nodes can be nested within each other
* Use [Collection](/university/core-components/collection) to iterate over a list of data
* A sitemap skeleton is available in the Marketplace

## Include the static sitemap

While you can use the autogenerated sitemap for static pages and create a separate sitemap for dynamic pages, you can also combine the two.

To include the static sitemap data in your custom sitemap, follow these steps:

1. Create a page
2. In the page settings, set the Document Type to XML
3. Set the page path to /sitemap.xml. This will override the default sitemap.
4. In **Settings > Variables**, add a variable and choose **Sitemap resource** in the **New variable** dialog.
5. Use a [Collection](/university/core-components/collection) to iterate over the static sitemap data

## Related

* [XML Time](/university/core-components/xml-time) – ISO date formatting for feeds
* [Collection](/university/core-components/collection) – Iterate over feed items
* [Head Slot](/university/core-components/head-slot) – Add meta tags


# XML Time

Format dates in XML sitemaps and RSS feeds using the XML Time component.

> See [MDN: Date.toISOString()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toISOString)

The XML Time component converts machine-readable date and time values to ISO 8601 format. This is essential for generating valid XML feeds like RSS, Atom, and sitemaps that require standardized date formats.

## When to Use

Use XML Time for:

* RSS feed publication dates (`<pubDate>`)
* Atom feed `<updated>` and `<published>` elements
* Sitemap `<lastmod>` timestamps
* Any XML output requiring ISO 8601 formatted dates

## How to Use

1. Drag an **XML Time** component from Components > XML onto your canvas
2. Bind the `datetime` property to a date value from your CMS or data source
3. The component outputs the date in ISO format (e.g., `2024-01-15T10:30:00.000Z`)

## Properties

| Property    | Type        | Description                                                                            |
| ----------- | ----------- | -------------------------------------------------------------------------------------- |
| `datetime`  | Date/String | The date value to convert. Accepts a JavaScript Date object, ISO string, or timestamp. |
| `dateStyle` | String      | Controls the output format style for the date.                                         |

## Example Usage

### RSS Feed Item Date

When building an RSS feed with a Collection, use XML Time to format publication dates:

```xml
<item>
  <title>Article Title</title>
  <pubDate>
    <!-- XML Time outputs: 2024-01-15T10:30:00.000Z -->
  </pubDate>
</item>
```

### Sitemap Last Modified

For XML sitemaps, XML Time ensures proper date formatting:

```xml
<url>
  <loc>https://example.com/page</loc>
  <lastmod>
    <!-- XML Time outputs the ISO date -->
  </lastmod>
</url>
```

## Binding Dynamic Dates

To display dynamic dates from your CMS:

1. Select the XML Time component
2. In Settings, click the binding icon next to `datetime`
3. Open the Expression editor
4. Bind to your date field: `Collection Item.publishedAt`

## Difference from Time Component

| Component    | Output                                             | Use Case                       |
| ------------ | -------------------------------------------------- | ------------------------------ |
| **Time**     | Human-readable dates (e.g., "January 15, 2024")    | Displaying dates to users      |
| **XML Time** | ISO 8601 format (e.g., "2024-01-15T10:30:00.000Z") | Machine-readable XML/RSS feeds |

## Technical Notes

* XML Time renders the date string directly without any HTML wrapper
* The ISO 8601 format is universally recognized by feed readers and XML parsers
* Always ensure your source date is valid; invalid dates will produce unexpected output

## Related Components

* [XML Node](/university/core-components/xml-node) – Create XML elements for feeds
* [Time](/university/core-components/time) – Display human-readable dates
* [Collection](/university/core-components/collection) – Loop through data for feed items


# Time

Display dates and times with semantic markup using the Time component in Webstudio.

> See [MDN: \<time>](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/time)

The Time component displays formatted dates and times with support for localization. It renders a semantic `<time>` HTML element and allows you to format dates according to different languages, countries, and formats.

## When to Use

Use Time for:

* Blog post publication dates
* Event dates and times
* Last updated timestamps
* Any date/time display that needs localization

## How to Use

1. Drag a **Time** component from Components > General onto your canvas
2. Set the `datetime` value (the actual date/time data)
3. Configure the display format using the formatting properties
4. Choose the timezone used to display the date
5. Optionally bind dynamic dates from your CMS

## Properties

### Edit the date in Settings

The **Date Time** control uses a calendar input for date-only values and a date/time input for timestamps. Timestamps with a time-zone offset are edited in UTC; the tooltip identifies this separately from the display time zone. Opening or focusing the control does not rewrite the stored value. Existing values that cannot be represented by the date input remain available as text so they can be repaired.

For an editable article, bind **Date Time** directly to the Content Block's document frontmatter field. Content-mode changes then save back to that file. Configure date formatting separately; formatting the binding expression makes it read-only.

Some commonly used properties (see the Settings panel for all available options):

### Core Properties

| Property     | Description                                                                     |
| ------------ | ------------------------------------------------------------------------------- |
| **datetime** | The date/time value (ISO 8601 format recommended)                               |
| **timeZone** | Timezone used to display the date, such as `UTC`, `visitor`, or `Europe/Berlin` |

### Formatting Properties

| Property      | Description                          | Example Values                    |
| ------------- | ------------------------------------ | --------------------------------- |
| **language**  | Language code for localization       | `en`, `es`, `fr`, `de`, `ja`      |
| **country**   | Country code for regional formatting | `US`, `GB`, `DE`, `JP`            |
| **dateStyle** | How to display the date              | `full`, `long`, `medium`, `short` |
| **timeStyle** | How to display the time              | `full`, `long`, `medium`, `short` |
| **format**    | Custom format using tokens           | `DDDD, MMMM DD, YYYY`             |

## Timezone

The Time component formats dates in `UTC` by default, which keeps output consistent across visitors and deployments.

Use the `timeZone` property when the displayed time should be tied to a specific location or to the visitor:

| Value         | Behavior                                                                                           |
| ------------- | -------------------------------------------------------------------------------------------------- |
| `UTC`         | Displays the date in Coordinated Universal Time                                                    |
| `visitor`     | Displays the date in each visitor's browser timezone after the page loads                          |
| IANA timezone | Displays the date in a fixed timezone such as `Europe/Berlin`, `America/New_York`, or `Asia/Tokyo` |

For events, webinars, launches, and deadlines, use a fixed IANA timezone so every visitor sees the time in the intended location. Use `visitor` when the time should adapt to each visitor's local timezone.

## Custom Date Formatting

For more control over date display, use the `format` property with these tokens:

### Date Tokens

| Token    | Description      | Example |
| -------- | ---------------- | ------- |
| **YYYY** | 4-digit year     | 2025    |
| **YY**   | 2-digit year     | 25      |
| **MMMM** | Full month name  | January |
| **MMM**  | Short month name | Jan     |
| **MM**   | 2-digit month    | 01      |
| **M**    | Month number     | 1       |
| **DDDD** | Full day name    | Monday  |
| **DDD**  | Short day name   | Mon     |
| **DD**   | 2-digit day      | 05      |
| **D**    | Day number       | 5       |

### Time Tokens

| Token  | Description       | Example |
| ------ | ----------------- | ------- |
| **HH** | 24-hour format    | 14      |
| **H**  | Hour (24-hour)    | 14      |
| **hh** | 12-hour format    | 02      |
| **h**  | Hour (12-hour)    | 2       |
| **mm** | Minutes           | 30      |
| **m**  | Minutes (no zero) | 30      |
| **ss** | Seconds           | 45      |
| **s**  | Seconds (no zero) | 45      |
| **a**  | am/pm             | pm      |
| **A**  | AM/PM             | PM      |

### Format Examples

For the date `2025-01-20T14:30:00`:

| Format                | Result                   |
| --------------------- | ------------------------ |
| `DDDD, MMMM DD, YYYY` | Monday, January 20, 2025 |
| `MMM DD, YYYY`        | Jan 20, 2025             |
| `DD/MM/YYYY`          | 20/01/2025               |
| `YYYY-MM-DD`          | 2025-01-20               |
| `DDD, MMM D`          | Mon, Jan 20              |
| `HH:mm:ss`            | 14:30:00                 |
| `h:mm A`              | 2:30 PM                  |

### Localized Names

Month and day names adapt to the `language` property:

* **en**: Monday, January
* **es**: lunes, enero
* **fr**: lundi, janvier
* **de**: Montag, Januar
* **ja**: 月曜日, 1月

{% hint style="info" %}
When using custom format tokens, month and day names are automatically localized based on the `language` property, making it easy to create multilingual sites.
{% endhint %}

## Date Style Examples

For the date `2025-01-20`:

| Style      | en-US                    | de-DE                   |
| ---------- | ------------------------ | ----------------------- |
| **full**   | Monday, January 20, 2025 | Montag, 20. Januar 2025 |
| **long**   | January 20, 2025         | 20. Januar 2025         |
| **medium** | Jan 20, 2025             | 20.01.2025              |
| **short**  | 1/20/25                  | 20.01.25                |

## Using with Dynamic Data

When binding dates from a CMS or API:

1. Bind the `datetime` property to your date field
2. Optionally bind `language` to a dynamic value for multilingual sites
3. Optionally bind `timeZone` if the CMS provides an IANA timezone
4. The component will format the date according to your settings

### Example Expression

```javascript
// If your CMS returns an ISO date string
myResource.publishedAt;
```

## Localization

The Time component is essential for multilingual sites:

1. Set the `language` property to match the page language
2. For dynamic language, bind to `system.params.lang` (if using language in URL)
3. The date will automatically format according to locale conventions

## Semantic HTML

Time renders as a `<time>` element with a `datetime` attribute:

```html
<time datetime="2025-01-20T10:30:00Z">January 20, 2025</time>
```

This helps search engines and assistive technologies understand the date.

## Related

* [XML Time](/university/core-components/xml-time) – ISO format for RSS/XML feeds
* [Collection](/university/core-components/collection) – Display dates from CMS data
* [Variables](/university/foundations/variables) – Bind dynamic date values

## Related Videos

{% embed url="<https://www.youtube.com/watch?v=EznQsyS5M5A>" %}


# Built with Webstudio

Display a 'Built with Webstudio' badge on your site.

The Built with Webstudio component displays a badge that links to Webstudio.

## Badge Behavior

The badge:

* Appears in a fixed position on the page (typically bottom corner)
* Contains the Webstudio logo/icon
* Links to `https://webstudio.is`
* Opens in a new tab when clicked

## Styling

While the badge has default styling, you can customize its appearance:

## Related

* [Gallery](https://webstudio.is/gallery) – Explore sites built with Webstudio
* [Project settings](/university/foundations/project-settings) – Manage your project's plan and settings
* [Publishing](/university/foundations/publishing-and-custom-domains) – Learn about publishing your site


# CLI

Webstudio's Command Line Interface (CLI) allows you to interact directly with your Projects from the command line.

Webstudio's CLI lets you work with Projects from the command line. You can export and build a Project, publish and manage domains, inspect Project permissions, capture screenshots, and expose the configured Project to automation tools through MCP.

For self-hosting, the CLI can export your entire Webstudio Project. Once exported, these projects are ready to be deployed on any hosting platform of your choice - giving you complete freedom over where and how your website goes live.

## Quick setup

The current Webstudio CLI package is `webstudio`. Run it with `npx`; do not install it globally or create a permanent `webstudio` command unless you intentionally need that. Do not use the old `@webstudio-is/cli` package or the old `wstd` command.

Use this page as the source of truth for the CLI package. Do not infer the package name or command from local repositories, old README files, or older examples.

First, check whether Node.js and npm are already installed:

```sh
node --version
npm --version
```

If `node` is version 22.12 or greater and `npm` prints a version, skip Node.js installation. If either command is missing, install Node.js 22.12 or greater before using the Webstudio CLI.

Run the latest Webstudio CLI without a global install:

```bash
npx --yes webstudio@latest --version
```

No separate Webstudio CLI installation is required when using `npx`. After verifying the latest CLI with `npx --yes webstudio@latest`, you can use the shorter `npx webstudio` command in the same environment.

For the latest version regardless of any existing global install, use `npx --yes webstudio@latest`.

## How to run Webstudio commands

Examples on this page use the no-install `npx webstudio` command:

```bash
npx webstudio <command> [options]
```

Use one of these command formats:

| Setup                       | Command format                                   |
| --------------------------- | ------------------------------------------------ |
| Latest one-off run, all OSs | `npx --yes webstudio@latest <command> [options]` |
| After verifying latest      | `npx webstudio <command> [options]`              |

Running with `npx` does not install a permanent `webstudio` command. Continue using `npx webstudio` for normal usage.

Use `npx webstudio --help` for the current command list.

## How to update the CLI

The recommended `npx` setup does not install a permanent copy of the Webstudio CLI, so there is no separate update command. Run the latest published version explicitly:

```bash
npx --yes webstudio@latest --version
```

Then use `@latest` with any command when you need to guarantee that run uses the newest release:

```bash
npx --yes webstudio@latest <command> [options]
```

This updates the CLI used by `npx`; it does not modify your linked Project or its `.webstudio` files until you run a command that does so. If you previously installed `webstudio` globally, remove that installation to avoid invoking an older permanent command:

```bash
npm uninstall --global webstudio
```

## First use

To use the CLI with a Project, you need an existing Webstudio Project and a Builder share link with Build access. Create the share link from the Share dialog in the Builder.

Run the guided flow:

```bash
npx webstudio
```

Or link a Project non-interactively:

```bash
npx webstudio link --link "<share-link-with-build-access>"
```

Sync exports the current saved Builder project and its asset files. Publishing is not required.

## Install Node.js only when needed

You need Node.js 22.12 or greater to use the Webstudio CLI. If `node --version` reports version 22.12 or greater, skip this section.

On macOS or Linux, you can install Node.js with NVM. First check whether NVM already exists:

```sh
command -v nvm
```

If `nvm` is not found, install it:

```sh
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.5/install.sh | bash
```

After installing NVM, restart your terminal. If `nvm` is still not found, source your shell profile and try again.

Once NVM is installed, you can install Node.js version 22 or greater by running:

```sh
nvm install 22
```

Verify your Node.js installation by checking its version:

```sh
node --version
```

On Windows, install Node.js 22.12 or greater from [nodejs.org](https://nodejs.org/) or use a Windows-compatible Node version manager. After installing Node.js, open a new terminal and verify:

```powershell
node --version
npm --version
```

## How the CLI connects to a Project

Most CLI commands operate on the single Project configured in the current directory.

* `.webstudio/config.json` stores the Project ID.
* The global Webstudio config stores the origin and share-link token.
* `npx webstudio sync` downloads the Project bundle to `.webstudio/data.json`.
* Synced asset files are stored in `.webstudio/assets`.

You can configure a Project interactively:

```bash
npx webstudio link
```

Or pass a share link directly:

```bash
npx webstudio link --link "<share-link>"
```

For automation, use `init` with JSON output:

```bash
npx webstudio init --link "<share-link>" --json
```

The share link should include Build access when you need to sync, build, import, or automate Project changes.

## Export and build a Project

After linking, sync the current saved Project locally:

```bash
npx webstudio sync
```

Sync refreshes the project from the server, downloads its asset files, and prepares the local content index. It does not combine an older published build with current assets.

To replace another Project with the synced Project:

```bash
npx webstudio import --to "<target-share-link-with-build-access>"
```

Import replaces the target's pages, assets, and asset folders. It preserves file formats and folder assignments; it does not convert Markdown files to MDX.

Build a dynamic app with a deployment template:

```bash
npx webstudio build --template docker
```

{% hint style="info" %}
See [export types](/university/self-hosting#export-types) for more information about JavaScript applications vs. static sites.
{% endhint %}

The CLI scaffolds the application, creates its routes and pages, and downloads assets. In the generated project, install dependencies and use the scripts in its `package.json` to develop or create a production build.

Build a static site with:

```bash
npx webstudio build --template ssg
```

Please review [the limitations](/university/self-hosting#static-site-limitations) of using the static site export instead of dynamic templates.

## Other workflows

The CLI can also:

* Import a synced Project into another Project.
* Preview an export and capture screenshots.
* Publish, unpublish, and inspect publishing jobs.
* Manage and verify custom domains.
* Audit accessibility, security, SEO, and performance settings.
* Expose a Project to AI agents and other automation through MCP.

Use the built-in help for current commands and options:

```bash
npx webstudio --help
npx webstudio <command> --help
```

## Automation and MCP

Webstudio MCP lets agents inspect and edit the native visual-builder model so their changes remain editable in the Builder. After linking a Project, shell-capable agents can call MCP tools directly without configuring or restarting an MCP client:

```bash
npx webstudio meta.index
npx webstudio insert-fragment '{"parentInstanceId":"<parent-id>","fragment":"<section />"}' --dry-run
```

The explicit equivalent is `npx webstudio mcp single-op-call <tool> '<json>'`. To keep Webstudio tools available inside a supported client, generate its persistent configuration:

```bash
npx webstudio connect claude
npx webstudio connect codex
npx webstudio connect cursor
npx webstudio connect vscode
```

See [Webstudio MCP](/university/mcp) for the exhaustive, versioned setup, discovery, editing, verification, safety, and troubleshooting reference generated from the latest published CLI manual.

## Related

* [Webstudio MCP](/university/mcp) – Connect agents to the native visual-builder model
* [Download](/university/self-hosting/download) – Export a static site directly from the Builder
* [Netlify](/university/self-hosting/netlify) – Deploy your project to Netlify
* [Vercel](/university/self-hosting/vercel) – Deploy your project to Vercel
* [VPS with Docker](/university/self-hosting/vps-with-docker) – Deploy to your own server using Docker
* [Publishing and custom domains](/university/foundations/publishing-and-custom-domains) – Set up custom domains for your site


# MCP

Connect AI agents and automation to a Webstudio Project through MCP or direct CLI tool calls.

**Webstudio MCP v0.307.0**

## Introduction

All you need to do is install the Codex desktop app or Claude Desktop and tell it:

> Install Webstudio CLI and use MCP.

***

{% hint style="info" %}
This reference is generated from the Webstudio CLI source in the same Builder revision. GitBook publishes it when that revision is successfully released. Examples use an installed `webstudio` command. See [CLI](/university/cli) for Node.js and `npx` setup.
{% endhint %}

`webstudio mcp` starts a stdio MCP server for real MCP clients. Shell users can call MCP tools with the shortcut form `webstudio <tool> '<json>'`, for example `webstudio meta.index` or `webstudio insert-fragment '<json>' --dry-run`. `webstudio mcp single-op-call` is the explicit equivalent and prints the structured JSON result. `webstudio mcp run` runs multiple MCP tool calls from inline JSON or a normal JSON file in one shared CLI session. Do not manually type or pipe raw JSON-RPC frames into `webstudio mcp` from an interactive shell or PTY.

## Startup

If you are already working with a shell-capable agent, it can use the local CLI directly. Native MCP client registration is optional. Give the editable Builder share link only when the trusted agent asks for it. Treat the share link as a credential: do not include it in committed files, screenshots, logs, or issue reports.

1. Configure a project with `webstudio init --link <api-share-link> --json`.
2. Check capabilities with `webstudio permissions --json`.
3. Use shortcut calls such as `webstudio meta.index` and `webstudio insert-fragment '<json>' --dry-run` for individual MCP tool calls. Use the explicit equivalent `webstudio mcp single-op-call <tool> '<json>'` when you need to force the MCP path, or `webstudio mcp run '[{"tool":"components.find","input":{"brief":"button"}}]'` for bounded multi-call workflows. Use `webstudio mcp run .temp/mcp-calls.json` for large batches.
4. Start discovery with `meta.index`, then call focused tools with concrete JSON, for example `webstudio mcp single-op-call meta.guide '{"brief":"Create a design system page using every component"}'`.

Do not run `webstudio sync`, install an MCP server, change client configuration, or restart the app for this local CLI workflow.

When the user explicitly wants persistent native MCP integration, run `webstudio connect claude`, `webstudio connect codex`, `webstudio connect cursor`, or `webstudio connect vscode`. This optional command changes client configuration, so follow its client-specific reload or restart instruction. Use `--print` to inspect the generated setup without changing configuration or requiring project access. For Codex, `connect` registers and verifies the server through the Codex CLI. Before changing client configuration, `connect` verifies that the saved project endpoint is reachable and its credential is accepted.

Start MCP from the linked Webstudio project root. The lifecycle status line prints that absolute root; create local scripts, screenshots, and temporary artifacts under that root, for example `<project root>/.temp/script.mjs`. If the shell starts in a parent workspace, `cd` into the project root first or use absolute paths.

When developing inside the Webstudio monorepo, start the local CLI exactly as `node packages/cli/local.js mcp` from the repo root. Do not use `pnpm exec webstudio`, `pnpm --filter webstudio exec webstudio`, or a global `webstudio`: they can resolve an older binary.

While the server is running, stdout is reserved for MCP JSON-RPC messages. Do not print human text from the server process. The server advertises MCP `logging` capability and emits sparse `notifications/message` logs for ready state and tool lifecycle checkpoints such as `tool preview.start started`, `tool preview.start still running after 10000ms`, and `tool preview.start succeeded in 1234ms`; stderr also mirrors these sparse lifecycle fallback lines prefixed with `[webstudio mcp]`.

## One-Shot Tool Calls

### Server-side Form simulation

`forms.simulate-submission` evaluates a saved Form using synthetic string fields and the same trusted Builder server Form handler used by Builder Preview. Supply the saved Form instance ID and page path. The server uses this project's publisher origin. It can trigger the Form's configured HTTP and Email Resources, so verify every destination and recipient first. For dynamic checkbox or multiple-select fields, pass `arrayNames` to preserve an empty or single-value array when the saved field props do not identify the control. The result reports destination IDs, statuses, and errors without Resource bodies. Managed Email errors include a code when available. This server simulation does not exercise client validation, browser event handlers, navigation, file bytes, or actual browser information. The Builder server loads the saved draft and holds the Email service credentials; the CLI does not receive those credentials.

```json
{
  "path": "/contact",
  "formId": "saved-form-instance-id",
  "values": [{ "name": "email", "value": "synthetic@example.com" }]
}
```

Use the shortcut `webstudio <tool> '<json>'` when you are operating from a shell and need one MCP tool result. The explicit form `webstudio mcp single-op-call <tool> '<json>'` is equivalent and avoids writing temporary Node.js stdio client scripts.

Examples:

```sh
webstudio mcp single-op-call meta.index
webstudio mcp single-op-call meta.guide '{"brief":"Create a design system page using every component"}'
webstudio mcp single-op-call meta.get-more-tools '{"tools":["insert-fragment"]}'
webstudio mcp single-op-call components.list '{"source":"all"}'
webstudio mcp single-op-call components.coverage-plan
webstudio mcp single-op-call components.search '{"brief":"radix select"}'
webstudio mcp single-op-call components.get '{"component":"@webstudio-is/sdk-components-react-radix:Select"}'
webstudio mcp single-op-call templates.list
webstudio mcp single-op-call templates.get '{"component":"@webstudio-is/sdk-components-react-radix:Select"}'
webstudio mcp single-op-call insert-fragment --input-file .temp/insert-fragment.json
```

Shortcut equivalents:

```sh
webstudio meta.index
webstudio meta.guide '{"brief":"Create a design system page using every component"}'
webstudio meta.get-more-tools '{"tools":["insert-fragment"]}'
webstudio components.list '{"source":"all"}'
webstudio components.coverage-plan
webstudio components.search '{"brief":"radix select"}'
webstudio components.get '{"component":"@webstudio-is/sdk-components-react-radix:Select"}'
webstudio templates.list
webstudio templates.get '{"component":"@webstudio-is/sdk-components-react-radix:Select"}'
webstudio insert-fragment --input-file .temp/insert-fragment.json
```

### Tool name convention

MCP tool names are opaque strings, not JavaScript property access. A dot separates a namespace from its tool name, and every segment uses lowercase kebab-case. For example, `components.coverage-insert-next` is the `coverage-insert-next` tool in the `components` namespace. Pass the complete name as one CLI argument: `webstudio components.coverage-insert-next`. Batch `mcp run` calls also accept the underscore form advertised by MCP protocol discovery, such as `components_coverage_insert_next`. Unknown names return near matches and direct you to `meta.index`.

### Readable fragment inputs

Prefer `--input-file` for JSX so JSON and shell quoting do not obscure the fragment. For example, save this as `.temp/insert-fragment.json`:

```json
{
  "parentInstanceId": "root-id",
  "fragment": "<section ws:style={css`padding: 32px; display: grid; gap: 16px;`}><h2>Northstar Product OS</h2><p>Reusable patterns for teams.</p></section>"
}
```

Then run `webstudio insert-fragment --input-file .temp/insert-fragment.json`. Single quotes inside the JSX keep the JSON valid and readable without backslash-escaped attributes.

Write and review larger fragments as JSX before placing them in the `fragment` field. Common patterns:

```tsx
<section style={{ padding: 32, borderRadius: 16 }}>
  <h2>Operations Console</h2>
  <p>
    React-style object styles become editable Webstudio styles.
  </p>
</section>

<section ws:tokens={[token("accent", css`color: #0f766e;`)]}>
  <button
    onClick={new ActionValue(["event"], expression`console.log(event)`)}
  >
    Track launch
  </button>
</section>

<section ws:tokens={[token("accent")]}>Reuse an existing accent token</section>

<section>
  <Switch>
    <SwitchThumb />
  </Switch>
</section>
```

Use the two-argument `token` form with a `css` template to define styles in the fragment. Use `token("name")` to attach an existing project token; an unknown name is an error.

Rules:

* Inside the Webstudio monorepo, replace `webstudio` in the examples above with `node packages/cli/local.js`, for example `node packages/cli/local.js meta.index`.
* For a simple authored/styled section, run `meta.index`, then `meta.get-more-tools '{"tools":["insert-fragment"]}'`, then `insert-fragment`. Do not grep source files, dump full MCP resources, or write parser scripts first.
* In `insert-fragment` JSX, use ``ws:style={css`...`}`` for Webstudio-native CSS, or use React-style object syntax such as `style={{ padding: 24 }}` when that is simpler. Both forms create editable Webstudio style data.
* `css` templates accept declarations and `@media` rules. Do not put selectors or unsupported at-rules such as `@keyframes` inside them. Use direct animation component identifiers such as `<AnimateChildren>`; `animation` is accepted only as legacy input and is not a callable CSS keyframes helper.
* Do not access host globals or dynamic code APIs in JSX fragments, including `process`, `globalThis`, `eval`, `Function`, or `constructor`.
* Use Webstudio prop names such as `class` and `for`; do not use React aliases `className` or `htmlFor`.
* Use Webstudio actions for event/action props, for example `onClick={new ActionValue(["event"], expression\`console.log(event)\`)}`. Do not pass JavaScript functions such as` onClick={() => ...}\`.
* Plain prop values must be JSON-compatible: `null`, strings, booleans, finite numbers, arrays, and plain objects. Do not pass `undefined`, `Symbol`, `BigInt`, `NaN`, `Infinity`, `Date`, `Map`, `Set`, class instances, or circular objects; omit the prop, use plain data, or use `expression`/`ActionValue` when the value is dynamic.
* Template-backed components used in JSX must include required child/part components explicitly under the same parent structure as the template, for example `<Switch><SwitchThumb /></Switch>`. Use `insert-component` when you want one automatic registered component template.
* The positional input is JSON and defaults to `{}`.
* Use `--input-file` for large mutation payloads.
* Use `--dry-run` with local-capable mutation tools when you need a patch plan without committing. The computed transaction is returned in `meta.session.transaction`, and `meta.session.version` is its base build version. Copying a `.webstudio` folder is not an isolated project clone; `.webstudio/config.json` still points to the same remote project, so non-dry-run mutations can commit to that project.
* The command prints JSON to stdout for both success and failure. Success uses the same `structuredContent` shape MCP tools return: `{ "ok": true, "data": ..., "meta": ... }`. Failure prints `{ "ok": false, "error": { "code": "...", "message": "..." }, "meta": ... }` and exits nonzero.
* The command writes sparse progress to stderr, including start, success/failure, elapsed time, and committed status when the tool returns session metadata.
* Invalid argument types fail loudly with path-specific messages, for example `meta.guide input.brief must be a string when provided`.
* Run one-shot shortcut or `mcp single-op-call` commands sequentially against the same linked `.webstudio` folder. If you receive `PROJECT_SESSION_BUSY`, another CLI/MCP process is updating the local session; wait a moment and retry sequentially.
* To work with another previously linked project without changing the directory's default link, start MCP or a shell call with `--project <projectId>`, for example `webstudio mcp --project <projectId>` or `webstudio mcp single-op-call list-pages --project <projectId>`. Selected projects use isolated local session and checkpoint files.
* If you are a delegated agent and your parent cannot see live stderr/stdout, do not run a long sequence of shortcut or `mcp single-op-call` commands silently and do not wrap many calls in a shell loop. Treat each parent-visible checkpoint as the unit of work. If the parent asks for status within 30 seconds, run exactly one `webstudio <tool>` or `webstudio mcp single-op-call` command, report that command/result, then wait before the next MCP command. For all-component design-system pages, checkpoint after discovery, checkpoint after page creation, call `components.coverage-insert-next` once before checkpointing again, then finish with the `presentation-pass` workflow phase. Coverage alone is not completion; organize examples into styled sections/cards.

## MDX-backed Content Blocks

Use a connected `.mdx` Asset when editors should change a Content Block body visually while the document remains stored as a file.

Choose this format before building a blog: each reachable connected MDX article is downloaded and compiled when publishing, so publishing can take longer as the blog grows. If editors only need to edit Markdown source in Assets, use `.md` with Markdown Embed instead. Content mode editors can edit those files in the Markdown file editor, but cannot edit the article body visually on the canvas. `.md` does not need per-article MDX compilation; the current publish sync still transfers Asset files for either format, and static generation may read Markdown bodies.

### Connect a collection to its entry page

When MDX files belong to a content collection, connect the collection to its dynamic entry page explicitly. Read the page ID after finding or creating the page, verify that its path has exactly one URL parameter, and store the ID at `x-webstudio.entryPageId` in `collection.json`. Do not assume Webstudio infers the page from its path, Assets query, or Content Block source. Preserve the rest of `collection.json`, and verify **Open on canvas** from both an entry asset menu and **Entry settings** before handoff. If no compatible page exists, leave the field unset and report that entry navigation still needs configuration.

```json
{
  "x-webstudio": {
    "template": "template.mdx",
    "entries": ["*.mdx"],
    "entryPageId": "<dynamic-entry-page-id>"
  }
}
```

1. Create the `.mdx` file under `.webstudio/assets`, then upload it and keep the returned Asset ID.
2. Inspect the Content Block and verify that it has exactly one direct Templates container. Every custom template referenced from MDX needs a unique top-level instance name. A missing or second Templates container blocks connected MDX materialization and publication.
3. Connect the Asset with `connect-content-block-source`. Use a stable page-based `renderScope` for a direct occurrence.
4. If the result returns `requiresConfirmation:true`, tell the user that connecting will replace the existing Body content. Repeat the same call with `confirmReplacement:true` only after approval.
5. Edit the complete source with `edit-content-block-source`, or replace the complete frontmatter map with `update-content-block-frontmatter`.
6. Inspect every returned diagnostic. Invalid MDX is preserved, not silently repaired.

```sh
webstudio upload-asset '{"asset":{"name":"article.mdx","type":"file","format":"mdx","meta":{}},"assetsDir":".webstudio/assets"}'
webstudio connect-content-block-source '{"blockInstanceId":"<contentBlockInstanceId>","renderScope":"page:/articles/example","source":{"type":"asset","assetId":"<mdxAssetId>"}}'
webstudio inspect-content-block-source '{"blockInstanceId":"<contentBlockInstanceId>","renderScope":"page:/articles/example"}'
webstudio edit-content-block-source --input-file .temp/edit-content-block-source.json
webstudio reload-content-block-source '{"blockInstanceId":"<contentBlockInstanceId>","renderScope":"page:/articles/example"}'
webstudio migrate-content-block-template-references '{"assetIds":["<mdxAssetId>"],"migration":{"type":"rename","from":"Old template name","to":"New template name"}}'
```

Prefer Markdown for standard document content. Markdown nodes automatically use a unique matching semantic template when one exists and keep their normal semantic fallback otherwise. Use lowercase JSX such as `<section>` or `<svg>` for a standard HTML or SVG element with authored properties Markdown cannot express.

Reference a uniquely named top-level custom template with capitalized JSX such as `<PromotionCard tone="featured">Content</PromotionCard>`. The template's stable **Name** must be a valid JSX component identifier and is independent from its display label. A template name wins over a built-in MDX adapter. Other registered components are unavailable until added to this Content Block's Templates. Attributes accept quoted static values and bare booleans. Expressions such as `{false}` are unsupported, as are imports, spreads, functions, and executable JavaScript. Legacy `ws.element` and `ws:name` forms are read for compatibility but are not emitted or recommended. Component namespaces such as `$.*`, `radix.*`, and `animation.*` are unsupported; use the direct component identifier.

When explicit JSX children match the designed template structure, their text and supported props overlay the cloned descendants so template styles stay intact. A mismatched child structure replaces the root defaults, and each authored child still resolves through a matching template when possible. An explicit empty pair such as `<PromotionCard></PromotionCard>` clears the defaults. A self-closing reference such as `<PromotionCard />` keeps them. Editing inherited default content writes it back as explicit JSX children. Template resolution is live: adding a missing semantic or named template later also updates existing MDX without rewriting it. Preserve unresolved template names and report their diagnostics.

When a template is renamed or deleted, use `migrate-content-block-template-references` to update the affected MDX files. Its first call returns a plan with changed-file, update, omission, and diagnostic counts. Report the plan and repeat the exact request with its `confirmationToken` only after approval. Renames and removals update named JSX and legacy `ws:name` references, including names such as `Image` and `CodeText` when they identify templates. Rename targets must be valid PascalCase JSX identifiers. Removing a paired reference unwraps and preserves its explicit authored children; removing a self-closing reference removes that node because it has no authored children. Invalid files remain unchanged and are reported in diagnostics.

Use the dedicated connect, switch, inspect, edit, update-frontmatter, reload, and disconnect operations instead of creating or deleting the Content Block's `src` with generic prop tools. An expression-bound source inside a Collection also needs the occurrence's scoped values and a distinct stable `renderScope`. For example, use `source:{"type":"expression","value":"post.assetId"}` with `variables:{"post":{"assetId":"<mdxAssetId>"}}`.

`renderScope` is any non-empty stable identity for one rendered occurrence, such as `page:/articles/example`. It does not load that page, its route parameters, or its resource results. To connect a result-one Assets resource, run `preview-asset-query` with concrete values, keep the saved source expression such as `post.data.id`, and supply the previewed item for this occurrence with `variables:{"post":{"data":{"id":"<mdxAssetId>"}}}`. The operation validates that concrete Asset while persisting the dynamic expression.

A source edit replaces the complete MDX document. A frontmatter update replaces the complete frontmatter property map. Inspect the current source first and preserve everything the user did not ask to change. Store frontmatter images as exact `$ref` objects. Bind editable Image sources to their resolved `.src` with explicit `binding.mode:"readwrite"`; omitting the mode leaves the image visible but not replaceable in Content mode. Bind alt properties to their Asset `.description` with `mode:"read"`. Use `update-content-block-frontmatter` for MCP frontmatter edits. MDX-rendered elements are not persistent instance targets for generic `bind-props` or `update-text` calls. Preserve existing `mode:"readwrite"` bindings when encountered; they are valid only for exact direct paths into the connected document's frontmatter. Direct bindings through a loaded Markdown or MDX `$ref` ending in `#frontmatter` save to the referenced file, with its write permissions enforced. Shared-record edits affect every document using that record. Computed expressions and JSON/body references remain read-only. Image replacement is supported: a direct Image source binding with `mode:"readwrite"` lets the picker replace the frontmatter `$ref`, while shared Asset metadata is edited in Asset settings.

For an editable MDX article, use the resource to select the Content Block's source Asset (`post.data.id`), then bind article fields inside the block to its document parameter (`document.frontmatter.title`), not query-result properties. Inspect the actual document variable name first. Create writable bindings explicitly: `update-text` uses `expressionBindingMode:"readwrite"`; `bind-props` uses `binding.mode:"readwrite"`. These generic tools target persistent designed instances, such as a header outside the MDX body, not MDX-generated instances. The MDX body is editable through its source mapping; merely placing static or query-bound content inside the block does not make it editable. Use exact direct paths; property access is already safe. Direct bindings through a loaded Markdown or MDX `$ref` ending in `#frontmatter` save to the referenced file, with its write permissions enforced. Shared-record edits affect every document using that record. Computed expressions and JSON/body references remain read-only. Image replacement is supported: a direct Image source binding with `mode:"readwrite"` lets the picker replace the frontmatter `$ref`, while shared Asset metadata is edited in Asset settings. Follow the complete article editability checklist below before handoff.

### Content Block completion checklist

For every Content Block creation, migration, or repair—with or without MDX—treat Content-mode editability as a delivery requirement for all content the user expects editors to change. Before implementation, inventory each value, its intended UI control, and its write destination. Choose controls by meaning: date/calendar controls for dates, image pickers for image replacement, and direct text or number controls for reading time. Keep fixed punctuation and units outside a directly bound value element; never mix literal and expression children there. Preserve the existing stored type and wording: if `readTime` already stores `4 min read`, bind that whole string directly instead of assuming it stores only a number. Distinguish protected layout/templates from editable content explicitly. Test each field through Content mode, check the saved project data or source file, reload, restore test values, and record passed/failed/not tested. Testing one heading, inspecting bindings, or successfully writing through MCP does not cover the remaining fields. Report missing controls and unsupported writes as unfinished requirements, not successful delivery.

### Complete article editability is required

When the user asks for the whole article to be editable in Content mode, this includes every article-owned value: title, excerpt, author details and links, dates, reading time, categories, hero and inline image sources, alternative text, captions, body text, links, and custom-component content and media. Exclude only designer-owned layout, styling, templates, and shared navigation/footer unless the user asks otherwise. A correct preview, a successful MCP write, or an editable body does not prove this requirement is met.

Before handoff:

1. Inventory every article field, its UI control, binding, and source file/field. Header fields outside **MDX content** are still part of the article and must be editable through the Content Block's document bindings.
2. In Content mode, edit every inventoried field through the UI, including choosing a different hero image and editing link destinations and custom-component props. Do not substitute a raw MDX/YAML edit or an MCP write for this check.
3. Read the saved source after each edit. Confirm the intended MDX body/frontmatter or referenced author file changed, unrelated content stayed intact, and the edit survives reload. Restore test values and verify restoration.
4. Mark each field passed, failed, or not tested. Do not claim the article is fully editable while any required field is failed or untested.

**Image replacement is not shared metadata editing.** For frontmatter such as `featureImage: { $ref: "./images/hero.png" }`, bind the Image source directly to `document.frontmatter.featureImage.src` with `binding.mode:"readwrite"` using `bind-props`. In Content mode, **Choose source** replaces the article’s `featureImage.$ref`; it does not overwrite the shared Asset’s resolved `.src`. The resolved URL field stays read-only. Verify the picker, saved reference, and reload—do not infer support from the displayed image alone. Bind alternative text to `.description` to use the shared Asset description; edit it through **Choose source → asset actions → Settings → Description**, which affects every use of that Asset. Do not treat a missing write mode as a platform limitation, and do not defer image replacement over a separate shared-description question.

Keep intended editable values separate from display formatting. For reading time, use three inline sibling text elements: static `—` , one value element bound directly to `document.frontmatter.readingTime` with `expressionBindingMode:"readwrite"`, and static `min read`. The value element must contain only that binding, not mixed literal and expression children. Preserve whitespace and the stored field type. Do not concatenate labels or units, use template literals, or add fallbacks/formatting calls to an editable binding. Prefer component formatting controls with a directly bound value. Explain unsupported cases and ask before making an intended editable field read-only; do not trade away editability just to match the display.

If an edit in a long-lived MCP session reports a revision conflict after another client saved the Asset, call `reload-content-block-source`, inspect the latest source, reapply the requested change, and retry. One-shot CLI calls refresh before each operation and normally cannot reproduce a stale session. Never overwrite the newer revision blindly. Use `disconnect-content-block-source` to remove the connection while leaving the Asset unchanged.

## Reporting CLI/MCP Issues

If a CLI/MCP tool gives a confusing error, crashes, hangs, produces invalid output, requires an undocumented workaround, or makes you inspect source code to understand normal usage, ask the user to report it in the Webstudio Discord `#help` channel: <https://wstd.us/community>.

Give the user a complete copy-paste report. Include only non-secret values: never include auth tokens, private URLs, cookies, API keys, passwords, or proprietary project data. Redact them as `<redacted>`.

Copy-paste template:

````md
Webstudio CLI/MCP issue report

What I was trying to do:
<short user goal, for example "Create a resource from an external API and render it in a collection">

What I expected:
<what should have happened>

What happened instead:
<exact error, confusing behavior, hang, missing docs, or workaround required>

Command/tool used:

```sh
<exact command or MCP tool call, with tokens/secrets redacted>
```

Structured output / error:

```json
<stdout JSON or MCP structuredContent, if available, with secrets redacted>
```

Stderr / lifecycle logs:

```txt
<stderr lines, timings, checkpoint messages, or stack trace, with secrets redacted>
```

Environment:

- CLI command path: <webstudio / node packages/cli/local.js / other>
- Webstudio CLI version: <from command output if known>
- OS: <macOS / Windows / Linux / unknown>
- Node version: <node -v if known>
- Project/session state: <linked project, local .webstudio session, preview, MCP server, or unknown>

Workaround tried:
<what the agent/user tried next, and whether it worked>

Why this should be improved:
<one sentence: better error message, docs, schema, tool behavior, etc.>
````

## Shared-Session Shell Runs

Use `webstudio mcp run '[{"tool":"components.find","input":{"brief":"button"}}]'` when you are operating from a shell and need several MCP tool calls to share one CLI session without hand-writing JSON-RPC. For large batches, pass a normal JSON file path such as `.temp/mcp-calls.json`. Do not use shell process substitution like `<(...)`; use inline JSON or a real file.

Use `mcp run` for long-lived tools such as `preview.start`. A one-shot `mcp single-op-call preview.start` cannot keep ownership of a preview server for a later screenshot or stop call. Put `preview.start`, `screenshot`, and `preview.stop` in one shared `mcp run` process, or use a real long-running MCP client.

Input shape:

```json
{
  "calls": [
    { "tool": "meta.index" },
    { "tool": "components.find", "input": { "brief": "radix select" } }
  ]
}
```

Rules:

* The command prints JSON to stdout for both success and failure. It stops at the first failed call and prints partial results in `{ "ok": false, "error": ..., "data": { "completedCalls": ..., "results": [...] }, "meta": ... }`, then exits nonzero.
* If a call returns `checkpoint.required`, read-only discovery and inspection remain available, but mutations and state-changing session tools return `CHECKPOINT_REQUIRED`. Stop and report the checkpoint to the parent/user. Only after the parent/user continues, call `checkpoint.ack {"reported":true,"continueAfterReport":true,"summary":"<what you reported>"}` before continuing mutations.
* For `mcp single-op-call`, checkpoint requirements persist across later one-shot CLI processes until you call `checkpoint.ack {"reported":true,"continueAfterReport":true,"summary":"<what you reported>"}`.
* Use this instead of manually sending JSON-RPC frames to `webstudio mcp` from a shell.

### Cross-project batches

Add `projects` to the same `mcp run` manifest to run focused reads, audits, or dry runs across independently linked project roots:

```json
{
  "concurrency": 2,
  "calls": [
    { "tool": "status" },
    { "tool": "audit", "input": {} },
    {
      "tool": "update-project-settings",
      "input": { "meta": { "siteName": "Reviewed" } },
      "dryRun": true
    }
  ],
  "projects": [
    { "id": "site-a", "root": "../site-a" },
    { "id": "site-b", "root": "../site-b" }
  ]
}
```

Project roots and an optional `progressFile` are resolved relative to the manifest file. Each project may provide its own `calls` instead of using the top-level calls. Each root must already be linked with its own `.webstudio/config.json`; the runner creates an independently authenticated ProjectSession and uses root-scoped session, audit, preview-data, and checkpoint paths without changing the process working directory.

Concurrency defaults to 2, is capped at 16, and can be set in the manifest or overridden with `--concurrency`. A failure is reported for that project while other projects continue. Progress is saved after every successful call; rerunning with the default `--resume` skips completed projects and starts failed projects after their last confirmed successful call. Reads and dry runs may be retried. A committed mutation interrupted after dispatch is marked `AMBIGUOUS_MUTATION_RESULT` and is never replayed automatically; inspect that project before deciding how to continue. Use `--no-resume` only to intentionally start the complete manifest over.

Committed mutation tools are rejected in a projects batch unless the command includes `--approve-mutations`. Review the complete manifest before granting approval. `--dry-run` applies to every call and does not require mutation approval. The final stdout object is compact: project counts, one status/error record per project, elapsed time, and the progress-file path rather than every tool result.

## Discovery

Use MCP itself after startup, or call the same tools with `webstudio mcp single-op-call`:

* `tools/list`: machine-readable available tools
* `resources/list`: available overview and full JSON resources
* `meta.index`: concise capability catalog
* `meta.guide`: workflow for a user goal; call with a string brief such as `{"brief":"Create a pricing page"}`
* `meta.get-more-tools`: detailed params, examples, namespaces, and local/server behavior; prefer exact names such as `{"tools":["insert-fragment"]}` when you know them
* `components.list`: compact registry metadata for visible components and templates; use a focused get tool for complete details
* `components.summary`: component counts by default; use `{"detail":"components","limit":20}` for paginated entries
* `components.coverage-plan`: compact paged plan for design-system coverage tasks that need every component; default returns counts plus the first root page, use `{"detail":"roots"}`, `{"detail":"parts"}`, or `{"detail":"full"}` for more
* `components.coverage-status`: page-specific covered/missing component report with `missingRoots` and `missingParts`
* `components.search`: focused component/template search by id, namespace, label, category, or content model
* `components.find`: compatibility alias for focused component search
* `components.get`: full metadata for one component id
* `templates.list`: compact metadata for template-backed insertions only
* `templates.get`: full registry item and payload metadata for one template
* `search-project`: find a known value or id with `webstudio search-project '{"query":"pricing"}'` or MCP `search-project {"query":"pricing"}`; use focused list/get tools when the target structure is unknown

`meta.guide` returns structured `routing` with the selected workflow and any broad context bundle it recommends. Authentication and design context bundles appear only when their specialized workflow is selected. Set `authoredFragment` when using an authored fragment and `reuseDesignSystem` when its recommendations should retain design-system discovery tools.

Set `taskScope` and `workflow` explicitly; `meta.guide` does not infer them or the authored-fragment flags from the brief. Specialized workflows are `markdown-blog`, `json-ld`, `collection`, `expression`, `authenticated-page`, `font-assets`, `design-input`, `content-block-source`, and `craft`; otherwise use `general`. Use `content-block-source` to connect or edit a Markdown/MDX-backed Content Block, including writable bindings for its designed header. For work that must not change project or local state, pass `{"brief":"Inventory custom code","taskScope":"read-only-audit","workflow":"general"}`. The resulting `read-only-discovery` workflow excludes mutation and side-effecting session tools and uses focused search, list, get, inspect, and snapshot tools.

`search-project` follows normal ProjectSession synchronization, then searches in the CLI process. Namespace filters limit values matched; related namespaces may still supply route and reference context, and synchronization is unchanged. Only paged matches enter model context. Recognized credential fields and asset binary or document bodies are excluded.

Component and template registry items use a shadcn-compatible top-level shape plus Webstudio-specific superset metadata in `meta`. Use `meta.runtime` for component ids, props, states, content model, and source identity; `meta.authoring` for composition and accessibility guidance; and `meta.builder` for template insertion details and expected project-data namespaces. These items are for Builder/MCP discovery and are not a published shadcn install registry yet.

Prefer the focused `components.*` tools over dumping `webstudio://project/components`. Do not write local scripts to parse full MCP discovery JSON for common component lookup. For “use every component” or design-system pages, start with compact `components.coverage-plan`, checkpoint, then page through roots/parts instead of dumping the full catalog.

## Consumer Capabilities

MCP lets agents work on one configured Webstudio project at a time. In consumer terms, agents can:

* Check which project they are connected to.
* Check what the share link is allowed to do.
* Inspect project metadata and the latest editable build.
* Read selected project data for audits and repair.
* Search all Builder namespaces for a known value or id without putting complete namespace data in model context.
* Apply precise project changes against a known version.
* List, inspect, create, update, delete, duplicate, copy, and reorder pages.
* Set the home page.
* Preserve old page paths for redirects or history.
* Read and update page titles, descriptions, metadata, auth settings, and SEO fields.
* List, create, update, duplicate, move, and delete page folders.
* List, create, update, delete, duplicate, reorder, and reuse page templates.
* Create pages from reusable templates.
* Read and update project site settings.
* Read and update marketplace product metadata.
* List, create, update, delete, and replace redirects.
* List, create, update, and delete responsive breakpoints.
* List and inspect page elements.
* Insert registered components.
* Insert styled JSX fragments.
* Move, reparent, clone, duplicate, wrap, unwrap, convert, rename, retag, and delete elements.
* Fill grid cells.
* List and update text children.
* Update plain text and expression text.
* Update structured rich text.
* Add, update, delete, and bind element props.
* Bind props to expressions, resources, actions, and runtime system values.
* Read, add, update, delete, and replace local styles.
* Update selected style-source styles.
* List, create, update, attach, detach, extract, duplicate, rename, lock, unlock, reorder, clear, and delete design tokens and style sources.
* List, define, rename, delete, and rewrite CSS variables.
* List, create, update, and delete static data variables.
* Create string, number, boolean, and JSON variables. Arrays use JSON.
* Delete unused data variables.
* List, create, update, upsert, bind, and delete resources.
* Create HTTP resources.
* Create GraphQL resources.
* Create system resources.
* Use built-in system resources for sitemap, current date, and assets.
* List and inspect complete asset metadata; upload, download, update, move, duplicate, find usage for, replace, and delete assets.
* List, create, rename, move, recursively duplicate, and recursively delete nested asset folders.
* Publish to staging or production.
* Publish to selected domains.
* List publish builds.
* Check publish job status after publishing.
* Unpublish staging or production deployments. Unpublish completes without a publish job.
* List, create, update, delete, and verify custom domains.
* Start and stop preview.
* Capture screenshots of generated pages.
* Compare screenshots against baselines.
* Install OCR support for richer visual checks.

Useful resources:

* `webstudio://project/status`: compact current ProjectSession status
* `webstudio://project/tools-overview`: small operation overview by capability area
* `webstudio://project/components-overview`: small component overview with ids, labels, namespaces, and categories
* `webstudio://project/tools`: full operation catalog; read only when focused metadata is insufficient
* `webstudio://project/components`: full component catalog with props, states, and content model composition constraints; read only when `components.summary`, `components.find`, and `components.get` are insufficient
* `webstudio://project/guide`: concise discovery guide
* `webstudio://project/expressions`: expression syntax, scope, supported methods, bindings, Collection iteration context, and verification
* `webstudio://project/accessibility-review`: evidence-based LLM accessibility-review workflow using project checks, preview, and screenshots

## MCP SDK Client Imports

When writing a local Node.js MCP client script, use the official MCP SDK package and these exact ESM imports:

Inside the Webstudio monorepo this package is available at the repo root. In another project, install it first with `pnpm add -D @modelcontextprotocol/sdk`.

```js
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
import { LoggingMessageNotificationSchema } from "@modelcontextprotocol/sdk/types.js";
```

Minimal stdio client for the local Webstudio CLI:

```js
const client = new Client({ name: "webstudio-agent", version: "1.0.0" });

client.setNotificationHandler(
  LoggingMessageNotificationSchema,
  (notification) => {
    console.error(`[mcp] ${notification.params.data}`);
  }
);

const transport = new StdioClientTransport({
  command: "node",
  args: ["packages/cli/local.js", "mcp"],
  cwd: process.cwd(),
  stderr: "inherit",
});

await client.connect(transport);

const index = await client.callTool({
  name: "meta.index",
  arguments: {},
});
console.log(JSON.stringify(index.structuredContent, null, 2));

await client.close();
```

Use `node packages/cli/local.js mcp` from the Webstudio monorepo root for local development, or `webstudio mcp` from a linked project where the CLI is installed. Keep stdout for JSON-RPC/structured results and surface MCP logging notifications or stderr lifecycle lines as progress.

## Core Rules

* stdout is reserved for MCP JSON-RPC while the server is running.
* Operate on the configured project only.
* Read ids before writing.
* Prefer semantic tools over `apply-patch`.
* Editor links with API access can edit Content Block text and props, insert or move eligible content, delete eligible instances, create pages from templates, change allowed page settings, upload assets, and create asset folders through semantic tools. The server checks each change against Content-mode rules. Raw `apply-patch` remains limited to text and editable props; other asset, folder, and design changes require Builder access.
* Use `status` and `refresh` when cached namespaces may be stale. Pass `status {"verbose":true}` only when debugging full namespace arrays, freshness, compatibility, or diagnostic details.
* Read `meta.session.commitStatus` before interpreting durability. Read-only results report `not-applicable` and retain `committed:false` for compatibility; dry-run plans report `planned`; failed mutations report `failed`; no-op mutations report `unchanged`; durable mutations report `committed` with `meta.session.committed:true`.
* Never run visual verification automatically. Ask first unless the user explicitly requested screenshots, visual verification, or a rendered audit; if they do not opt in, use focused non-visual assertions.

### Converting a composite component to HTML

`convert-instance` changes one instance and validates its entire descendant tree after that change. For a Tabs tree, changing `Tabs` to a `div` first fails because `TabsList`, `TabsTrigger`, and `TabsContent` still require their composite parents. Convert from the leaves upward instead: `TabsTrigger` to `ws:element` with tag `button`, `TabsContent` to `ws:element` with tag `div`, `TabsList` to `ws:element` with tag `div`, then `Tabs` to `ws:element` with tag `div`. Pass the complete, current `instanceSelector` for each call, starting with the target. Read the actual tree first; convert every remaining required part before its parent. Each step is a separate mutation, so this recipe is not atomic.

Conversion retains instance ids, children (including editable copy), styles, and most props; it removes a legacy `tag` prop and normalizes React prop aliases when converting to `ws:element`. Review component-specific props and behavior afterward: replacing Tabs with HTML does not recreate tab selection, keyboard behavior, or accessibility semantics. If a project requires the whole tree to change in one commit, use `snapshot` to read the current build version and exact records, then a single versioned `apply-patch` transaction for the existing instance component/tag fields. Preserve all existing ids and child references, include any needed prop changes in that same transaction, and verify the final tree.

For composite media such as Vimeo, inspect the player subtree and surrounding content first. A native `video` cannot retain provider-only parts such as `VimeoPlayButton`, `VimeoSpinner`, or `VimeoPreviewImage`. Use `delete-instance` with `instanceIds` containing the exact obsolete part ids (deleting a part also cleans its descendants and owned data), then `convert-instance` on the player root with `component: "ws:element"`, `tag: "video"`, and its current `instanceSelector`. Keep surrounding wrappers, captions, and other content outside the deletion set; move any content that must survive out of an obsolete part before deleting it. The player keeps its id and styles. Remove the provider-specific props with `delete-props`, using `{"deletions":[{"instanceId":"<player-id>","name":"url"}]}` for the old Vimeo URL prop and adding one entry for each other obsolete prop found during inspection. Then use `update-props` to set the native video source and playback props, and `update-styles` for any styling changes. A native video source must be a playable media URL or asset, not a Vimeo watch-page URL. These are separate semantic mutations; no raw instance patch is needed for this sequence.

## Vision Verification Loop

Vision-capable AI can use MCP to see what it is building:

1. Ask the user whether they want visual verification unless they explicitly requested it. Stop before calling preview, capture, comparison, OCR, or rendered-audit tools until the user opts in.
2. Make focused page/content/style changes with semantic MCP tools.
3. Call preview\.start once to keep the iterative generated site running. In shell-driven workflows, run preview\.start, screenshot, and preview\.stop inside one `webstudio mcp run` call so they share the same preview owner.
4. Read `preview.status.stale` before relying on generated output. When present, `renderedProjectVersion` identifies the last project version materialized into the preview; a stale preview refreshes automatically on the next managed screenshot or `preview.start` call.
5. `preview.start` and `webstudio preview` install generated app dependencies under `.webstudio/preview` and reuse them across regenerations.
6. Session previews download missing project assets into `.webstudio/assets`. If `PREVIEW_ASSET_DOWNLOAD_FAILED` occurs, restore network and project asset access, then retry `preview.start`.
7. When launcher metadata is available, dependency installation reuses a supported npm or pnpm launcher. Without launcher metadata, it defaults to npm.
8. When npm is selected, dependency installation honors `npm_config_cache`. If it is unset, preview uses the writable `.webstudio/preview/.npm-cache` directory instead of the user's default npm cache.
9. For npm cache permission errors, unset `npm_config_cache` to use the preview-local cache, then retry. No cache deletion is required.
10. Do not add generated-preview dependencies to the repository root `package.json` or `pnpm-lock.yaml`.
11. If dependency installation fails, the error includes the selected package-manager path and sanitized diagnostics. Check that path and the network configuration, then reinstall or update the Webstudio CLI if the problem persists.
12. After MCP mutations, path-based screenshots regenerate the current session in place, wait for its exact project version, and normally reload the route. The server and browser remain alive. From one-shot shell calls or another process, pass `baseUrl` with `path` to capture an already-running generated site without starting it. Use preview\.stop only in the same long-running MCP server or `webstudio mcp run` process that started preview; a separate one-shot `single-op-call` process does not own another process's preview controller.
13. For multi-page work, capture each changed page by path through the same preview server, for example screenshot({ path: "/" }), screenshot({ path: "/pricing" }), and screenshot({ path: "/about" }). The screenshot tool navigates directly to the requested route; no browser click navigation is required.
14. For responsive work, call list-breakpoints first, then capture screenshots at viewport widths based on the Builder breakpoints plus a narrow mobile and desktop width.
15. Call screenshot with { path: "/" } or the changed page path and viewport such as { width: 375, height: 812 } and { width: 1440, height: 900 }. For an existing preview in another process, call screenshot with { baseUrl: "<http://127.0.0.1:5177>", path: "/" }. Use waitForSelector when the page has a reliable ready marker, waitUntil:"networkidle" for network-heavy pages, and waitForTimeout only for final visual settling.
16. The MCP runner selects an available loopback port and returns the preview URL. Do not pass `host` or `port` to MCP preview or screenshot tools. To capture a generated site already running in another process, pass its `baseUrl` with `path`.
17. Automatic browser discovery checks system installations, configured browser paths, and Chromium installations in the Playwright browser cache.
18. The screenshot timeout bounds browser capture after the preview is ready. A timeout returns `SCREENSHOT_TIMEOUT`, resets the reusable browser session, and releases the shared preview lifecycle for cleanup.
19. When a baseline PNG exists, call screenshot.diff with baselinePath, currentPath, and outputDir for each page/viewport pair. Add expectedText when a specific visible phrase must be present; its assertions report pass/fail plus found and missing text. Add expectedVisual to set pass/fail limits for mismatch percentage, the number of changed regions, or an overall dominant color/brightness direction.
20. Read screenshot.diff textAnalysis: it reports OCR status plus text that appeared, disappeared, moved, changed content, or changed font/style geometry. If OCR is unavailable, expectedText assertions fail and textAnalysis reports why; ask the user for permission to install Tesseract, then call vision.install-ocr with { "confirm": true }, or rely on visual inspection.
21. Inspect every viewport PNG and any diff artifacts with vision, then compare layout, OCR text evidence, color, spacing, imagery, and responsive framing against the user intent.
22. If the screenshot does not match, apply another focused mutation and repeat screenshot verification.

Generated app setup:

* `preview.start` and `webstudio preview` install generated app dependencies under `.webstudio/preview` and reuse them across regenerations.
* Session previews download missing project assets into `.webstudio/assets`. If `PREVIEW_ASSET_DOWNLOAD_FAILED` occurs, restore network and project asset access, then retry `preview.start`.
* When launcher metadata is available, dependency installation reuses a supported npm or pnpm launcher. Without launcher metadata, it defaults to npm.
* When npm is selected, dependency installation honors `npm_config_cache`. If it is unset, preview uses the writable `.webstudio/preview/.npm-cache` directory instead of the user's default npm cache.
* For npm cache permission errors, unset `npm_config_cache` to use the preview-local cache, then retry. No cache deletion is required.
* Do not add generated-preview dependencies to the repository root `package.json` or `pnpm-lock.yaml`.
* If dependency installation fails, the error includes the selected package-manager path and sanitized diagnostics. Check that path and the network configuration, then reinstall or update the Webstudio CLI if the problem persists.

## MCP argument examples

Examples below show meaningful argument combinations. Tool schemas are the source of truth. For tools with no required arguments, pass `{}`.

### meta.guide

```json
{
  "brief": "Create a pricing page and style the hero",
  "taskScope": "visual-change",
  "workflow": "general"
}
```

```json
{
  "brief": "Inventory custom code without changing the project",
  "taskScope": "read-only-audit",
  "workflow": "general"
}
```

```json
{
  "brief": "Recreate a supplied design as a responsive page",
  "taskScope": "visual-change",
  "workflow": "design-input"
}
```

### verify-font-assets

```json
{
  "assetIds": [
    "asset-regular",
    "asset-bold"
  ]
}
```

### workflow\.next

```json
{
  "goal": "design-system-page"
}
```

```json
{
  "goal": "design-system-page",
  "phase": "dry-run-section"
}
```

### meta.get-more-tools

```json
{
  "tools": [
    "insert-fragment"
  ]
}
```

```json
{
  "tools": [
    "insert-component"
  ]
}
```

```json
{
  "brief": "update-styles"
}
```

### components.list

```json
{
  "source": "all",
  "documentType": "html"
}
```

### components.coverage-plan

```json
{
  "documentType": "html"
}
```

```json
{
  "documentType": "xml",
  "detail": "roots"
}
```

```json
{
  "detail": "full"
}
```

```json
{
  "detail": "roots",
  "offset": 0,
  "limit": 20
}
```

```json
{
  "detail": "parts",
  "namespace": "@webstudio-is/sdk-components-react-radix"
}
```

### components.coverage-status

```json
{
  "pagePath": "/design-system"
}
```

### components.coverage-insert-next

```json
{
  "pagePath": "/design-system",
  "parentInstanceId": "root-instance-id"
}
```

### components.find

```json
{
  "brief": "radix tabs dialog select"
}
```

### components.search

```json
{
  "brief": "radix tabs dialog select"
}
```

### components.get

```json
{
  "component": "@webstudio-is/sdk-components-react-radix:Select"
}
```

### templates.list

```json
{
  "documentType": "html"
}
```

### templates.get

```json
{
  "component": "@webstudio-is/sdk-components-react-radix:Select"
}
```

### refresh

```json
{
  "namespaces": [
    "pages",
    "instances",
    "styles"
  ]
}
```

### import

```json
{
  "to": "https://p-destination-project-id.wstd.dev/?authToken=destination-token"
}
```

### download-asset

```json
{
  "assetId": "asset-id"
}
```

### upload-asset

```json
{
  "asset": {
    "name": "Rajdhani-SemiBold.woff2",
    "type": "font",
    "format": "woff2",
    "meta": {
      "family": "Rajdhani",
      "style": "normal",
      "weight": 600
    }
  },
  "assetsDir": ".webstudio/assets"
}
```

```json
{
  "asset": {
    "name": "hero.png",
    "type": "image",
    "format": "png",
    "folderId": "folder-id",
    "meta": {
      "width": 1200,
      "height": 630
    }
  },
  "assetsDir": ".webstudio/assets"
}
```

```json
{
  "asset": {
    "name": "hero.png",
    "type": "image",
    "format": "png",
    "meta": {
      "width": 1200,
      "height": 630
    },
    "force": true
  },
  "assetsDir": ".webstudio/assets"
}
```

### upload-assets

```json
{
  "assets": [
    {
      "name": "hero.png",
      "type": "image",
      "format": "png",
      "folderId": "folder-id",
      "meta": {
        "width": 1200,
        "height": 630
      }
    }
  ],
  "assetsDir": ".webstudio/assets"
}
```

### create-asset-folder

```json
{
  "name": "Marketing"
}
```

```json
{
  "name": "Photos",
  "parentId": "marketing-folder-id"
}
```

### update-asset-folder

```json
{
  "folderId": "folder-id",
  "values": {
    "name": "Brand"
  }
}
```

```json
{
  "folderId": "folder-id",
  "values": {
    "parentId": null
  }
}
```

### duplicate-asset-folder

```json
{
  "folderId": "folder-id"
}
```

```json
{
  "folderId": "folder-id",
  "parentId": "target-folder-id"
}
```

### delete-asset-folder

```json
{
  "folderId": "folder-id"
}
```

### get-asset

```json
{
  "assetId": "asset-id"
}
```

### duplicate-asset

```json
{
  "assetId": "asset-id"
}
```

```json
{
  "assetId": "asset-id",
  "folderId": "target-folder-id"
}
```

### preview\.start

```json
{
  "source": "session"
}
```

### preview\.submit-form

```json
{
  "path": "/contact",
  "selector": "form",
  "values": [
    {
      "name": "email",
      "value": "me@example.com"
    }
  ]
}
```

### status

```json
{
  "verbose": true
}
```

### list-pages

```json
{
  "limit": 20
}
```

### get-page-by-path

```json
{
  "path": "/pricing"
}
```

### list-instances

```json
{
  "pagePath": "/",
  "maxDepth": 3
}
```

### inspect-instance

```json
{
  "instanceId": "instance-id",
  "include": [
    "props",
    "styles",
    "children"
  ]
}
```

### search-project

```json
{
  "query": "pricing"
}
```

```json
{
  "query": "api.example.com",
  "namespaces": [
    "resources"
  ]
}
```

### audit

```json
{
  "scopes": [
    "accessibility",
    "seo"
  ]
}
```

```json
{
  "pagePath": "/pricing",
  "severities": [
    "error",
    "warning"
  ]
}
```

```json
{
  "scopes": [
    "accessibility"
  ],
  "verbose": true
}
```

### report-issue

```json
{
  "trigger": "user-requested",
  "category": "schema-or-docs-mismatch",
  "deduplicationKey": "update-props-input-contract",
  "title": "fix: Clarify the update-props input contract",
  "agent": {
    "client": "Codex",
    "provider": "OpenAI",
    "model": "gpt-5.6-sol",
    "reasoningEffort": "medium"
  },
  "report": {
    "userStory": "As a Webstudio user, I want routine MCP edits to complete without corrective retries.",
    "summary": "A documented operation required a corrected retry.",
    "attemptedWorkflow": [
      "Inspect the target component.",
      "Attempt the update with the advertised tool."
    ],
    "expectedBehavior": "The documented input should be accepted.",
    "actualResult": "The initial call returned BAD_REQUEST.",
    "recoveryAttempts": [
      "Inspect the schema and retry with corrected input nesting."
    ],
    "userImpact": "The edit required extra tool calls.",
    "technicalContext": "The update-props input shape was ambiguous.",
    "acceptanceCriteria": [
      "The exposed schema matches runtime validation.",
      "A regression test covers the workflow."
    ]
  }
}
```

### insert-component

```json
{
  "parentInstanceId": "parent-id",
  "component": "@webstudio-is/sdk-components-react-radix:Switch"
}
```

### extract-slot

```json
{
  "instanceSelector": [
    "header-section-id",
    "body-id"
  ],
  "label": "Site header"
}
```

```json
{
  "instanceSelector": [
    "header-section-id",
    "page-wrapper-id",
    "body-id"
  ],
  "label": "Site header"
}
```

### insert-collection

```json
{
  "parentInstanceId": "parent-id",
  "data": {
    "type": "expression",
    "value": "Posts.data.items"
  },
  "itemFragment": "<article><h2>{expression`collectionItem.title ?? 'Untitled'`}</h2></article>"
}
```

```json
{
  "parentInstanceId": "parent-id",
  "data": {
    "type": "json",
    "value": [
      {
        "name": "Starter"
      },
      {
        "name": "Pro"
      }
    ]
  },
  "itemFragment": "<div>{expression`collectionItem.name`}</div>"
}
```

### insert-fragment

```json
{
  "parentInstanceId": "parent-id",
  "fragment": "<section ws:style={css`padding: 32px; display: grid; gap: 16px;`}><h2>Northstar Product OS</h2><p>Reusable patterns for teams.</p></section>"
}
```

```json
{
  "parentInstanceId": "parent-id",
  "fragment": "<section style={{ padding: 32, borderRadius: 16 }}><h2>Operations Console</h2><p>Semantic section with React-style object styles converted into editable Webstudio styles.</p></section>"
}
```

```json
{
  "parentInstanceId": "parent-id",
  "fragment": "<section ws:tokens={[token('accent', css`color: #0f766e;`)]} ws:style={css`display: grid; gap: 12px;`}><h2>Token Example</h2><button onClick={new ActionValue(['event'], expression`console.log(event)`)}>Track launch</button></section>"
}
```

```json
{
  "parentInstanceId": "parent-id",
  "fragment": "<section ws:tokens={[token('accent')]}><h2>Reuse an existing accent token</h2></section>"
}
```

```json
{
  "parentInstanceId": "parent-id",
  "fragment": "<section><Switch><SwitchThumb /></Switch></section>"
}
```

### insert-fragment-verified

```json
{
  "parentInstanceId": "parent-id",
  "pagePath": "/pricing",
  "fragment": "<section><h2>Pricing</h2></section>"
}
```

### update-text

```json
{
  "instanceId": "instance-id",
  "childIndex": 0,
  "text": "Launch faster",
  "mode": "text"
}
```

```json
{
  "instanceId": "instance-id",
  "childIndex": 0,
  "text": "user.name",
  "mode": "expression"
}
```

### replace-text

```json
{
  "find": "Start free",
  "replace": "Get started",
  "match": "exact",
  "pagePath": "/pricing",
  "limit": 20
}
```

### replace-prop-text

```json
{
  "find": "old.example.com",
  "replace": "www.example.com",
  "match": "substring",
  "names": [
    "href",
    "code"
  ],
  "limit": 20
}
```

### update-page

```json
{
  "pageId": "page-id",
  "values": {
    "title": "\"Pricing\"",
    "meta": {
      "description": "\"Pricing plans\""
    }
  }
}
```

### update-props

```json
{
  "updates": [
    {
      "instanceId": "button-id",
      "name": "aria-label",
      "type": "string",
      "value": "Open menu"
    },
    {
      "instanceId": "textarea-id",
      "name": "placeholder",
      "type": "string",
      "value": "Describe your project"
    }
  ]
}
```

### bind-props

```json
{
  "bindings": [
    {
      "instanceId": "link-id",
      "name": "href",
      "binding": {
        "type": "expression",
        "value": "currentPost.url"
      }
    }
  ]
}
```

### list-css-variables

```json
{
  "withUsage": true
}
```

### define-css-variable

```json
{
  "vars": {
    "--color-primary": "#2d3748",
    "--color-accent": "#e53e3e",
    "--space-card": "1.5rem"
  },
  "overwrite": true
}
```

### delete-css-variable

```json
{
  "names": [
    "--color-primary",
    "--color-accent",
    "--space-card"
  ],
  "force": true
}
```

### create-variable

```json
{
  "scopeInstanceId": "body-id",
  "name": "title",
  "value": {
    "type": "string",
    "value": "Hello"
  }
}
```

```json
{
  "scopeInstanceId": "body-id",
  "name": "count",
  "value": {
    "type": "number",
    "value": 3
  }
}
```

```json
{
  "scopeInstanceId": "body-id",
  "name": "featured",
  "value": {
    "type": "boolean",
    "value": true
  }
}
```

```json
{
  "scopeInstanceId": "body-id",
  "name": "tags",
  "value": {
    "type": "json",
    "value": [
      "news",
      "product"
    ]
  }
}
```

```json
{
  "scopeInstanceId": "body-id",
  "name": "filters",
  "value": {
    "type": "json",
    "value": {
      "tag": "news",
      "page": 1
    }
  }
}
```

### update-variable

```json
{
  "dataSourceId": "variable-id",
  "values": {
    "value": {
      "type": "json",
      "value": [
        "news",
        "product"
      ]
    }
  }
}
```

### create-resource

```json
{
  "resource": {
    "name": "Posts",
    "method": "get",
    "url": "https://api.example.com/posts",
    "headers": []
  }
}
```

```json
{
  "resource": {
    "name": "Filtered Posts",
    "method": "get",
    "url": "https://api.example.com/posts",
    "searchParams": [
      {
        "name": "tag",
        "value": "filters.tag"
      },
      {
        "name": "source",
        "value": {
          "type": "literal",
          "value": "website"
        }
      },
      {
        "name": "page",
        "value": "(filters.page ?? 1).toString()"
      }
    ],
    "headers": [
      {
        "name": "Authorization",
        "value": "\"Bearer \" + auth.token"
      }
    ]
  },
  "scopeInstanceId": "body-id",
  "dataSourceName": "posts"
}
```

```json
{
  "resource": {
    "name": "Post GraphQL",
    "control": "graphql",
    "method": "post",
    "url": "https://api.example.com/graphql",
    "headers": [
      {
        "name": "Content-Type",
        "value": {
          "type": "literal",
          "value": "application/json"
        }
      }
    ],
    "body": "{ query: \"query Post($slug: String!) { post(slug: $slug) { title } }\", variables: { slug: system.params.slug } }"
  },
  "scopeInstanceId": "body-id",
  "dataSourceName": "post",
  "exposeAsDataSource": true
}
```

```json
{
  "resource": {
    "name": "Current Date",
    "control": "system",
    "method": "get",
    "url": "/$resources/current-date",
    "headers": []
  },
  "scopeInstanceId": "body-id",
  "dataSourceName": "currentDate"
}
```

### update-resource

```json
{
  "resourceId": "resource-id",
  "values": {
    "url": "https://api.example.com/posts"
  }
}
```

```json
{
  "resourceId": "resource-id",
  "values": {
    "method": "post"
  },
  "exposeAsDataSource": false
}
```

### get-assets-resource

```json
{
  "resourceId": "resource-id"
}
```

### create-assets-resource

```json
{
  "name": "All assets",
  "scopeInstanceId": "body-id",
  "dataSourceName": "assets"
}
```

```json
{
  "name": "Published posts",
  "scopeInstanceId": "body-id",
  "dataSourceName": "posts",
  "query": {
    "result": "many",
    "where": {
      "all": [
        {
          "field": [
            "extension"
          ],
          "operator": "eq",
          "value": {
            "type": "literal",
            "value": "md"
          }
        },
        {
          "field": [
            "folderId"
          ],
          "operator": "eq",
          "value": {
            "type": "literal",
            "value": "folder-id"
          }
        },
        {
          "field": [
            "properties",
            "draft"
          ],
          "operator": "ne",
          "value": {
            "type": "literal",
            "value": true
          }
        }
      ]
    },
    "sort": [
      {
        "field": [
          "properties",
          "publishedAt"
        ],
        "direction": "desc"
      },
      {
        "field": [
          "id"
        ],
        "direction": "asc"
      }
    ],
    "limit": {
      "type": "literal",
      "value": 20
    },
    "offset": {
      "type": "literal",
      "value": 0
    },
    "output": {
      "mode": "fields",
      "includeMetadata": false,
      "fields": [
        [
          "properties",
          "title"
        ],
        [
          "properties",
          "slug"
        ],
        [
          "properties",
          "publishedAt"
        ],
        [
          "properties",
          "excerpt"
        ]
      ]
    },
    "content": {
      "mode": "none"
    }
  }
}
```

```json
{
  "name": "Post by slug",
  "scopeInstanceId": "body-id",
  "dataSourceName": "post",
  "query": {
    "result": "one",
    "where": {
      "all": [
        {
          "field": [
            "extension"
          ],
          "operator": "eq",
          "value": {
            "type": "literal",
            "value": "md"
          }
        },
        {
          "field": [
            "folderId"
          ],
          "operator": "eq",
          "value": {
            "type": "literal",
            "value": "folder-id"
          }
        },
        {
          "field": [
            "properties",
            "slug"
          ],
          "operator": "eq",
          "value": "system.params.slug"
        },
        {
          "field": [
            "properties",
            "draft"
          ],
          "operator": "ne",
          "value": {
            "type": "literal",
            "value": true
          }
        }
      ]
    },
    "output": {
      "mode": "fields",
      "includeMetadata": false,
      "fields": [
        [
          "properties",
          "title"
        ],
        [
          "properties",
          "publishedAt"
        ],
        [
          "properties",
          "excerpt"
        ],
        [
          "properties",
          "featureImage"
        ]
      ]
    },
    "content": {
      "mode": "markdown-body-ref"
    }
  }
}
```

### update-assets-resource

```json
{
  "resourceId": "resource-id",
  "values": {
    "query": {
      "limit": "50"
    }
  }
}
```

```json
{
  "resourceId": "resource-id",
  "values": {
    "query": null
  }
}
```

### validate-asset-query

```json
{
  "query": {
    "where": {
      "all": [
        {
          "field": [
            "properties",
            "slug"
          ],
          "operator": "eq",
          "value": "hello-world"
        }
      ]
    },
    "limit": 1
  }
}
```

### preview-asset-query

```json
{
  "query": {
    "result": "one",
    "where": {
      "all": [
        {
          "field": [
            "extension"
          ],
          "operator": "eq",
          "value": "md"
        },
        {
          "field": [
            "properties",
            "slug"
          ],
          "operator": "eq",
          "value": "hello-world"
        }
      ]
    },
    "output": {
      "mode": "fields",
      "includeMetadata": false,
      "fields": [
        [
          "properties",
          "title"
        ]
      ]
    },
    "content": {
      "mode": "markdown-body-ref",
      "maxBytes": 1048576
    }
  }
}
```

### update-asset

```json
{
  "assetId": "font-asset-id",
  "values": {
    "meta": {
      "family": "Rajdhani",
      "style": "normal",
      "weight": 600
    }
  }
}
```

```json
{
  "assetId": "asset-id",
  "values": {
    "description": "Team collaborating around a whiteboard"
  }
}
```

```json
{
  "assetId": "asset-id",
  "values": {
    "filename": "hero",
    "folderId": "folder-id"
  }
}
```

```json
{
  "assetId": "asset-id",
  "values": {
    "folderId": null
  }
}
```

### list-assets

```json
{
  "verbose": true
}
```

### replace-asset

```json
{
  "fromAssetId": "old-asset-id",
  "toAssetId": "new-asset-id"
}
```

### delete-asset

```json
{
  "assetIds": [
    "asset-id"
  ]
}
```

```json
{
  "assetIdPrefixes": [
    "generated-prefix"
  ]
}
```

### set-image-descriptions

```json
{
  "updates": [
    {
      "assetId": "hero-asset-id",
      "description": "Team collaborating around a whiteboard"
    },
    {
      "assetId": "background-texture-id",
      "decorative": true
    }
  ]
}
```

### replace-resource-text

```json
{
  "find": "api.old.example.com",
  "replace": "api.example.com",
  "fields": [
    "url"
  ],
  "limit": 20
}
```

### update-styles

```json
{
  "updates": [
    {
      "instanceId": "instance-id",
      "property": "color",
      "value": {
        "type": "keyword",
        "value": "red"
      }
    }
  ]
}
```

### delete-styles

```json
{
  "deletions": [
    {
      "instanceId": "instance-id",
      "property": "box-shadow"
    }
  ]
}
```

### apply-patch

```json
{
  "baseVersion": 12,
  "transactions": [
    {
      "id": "patch-transaction-label",
      "payload": [
        {
          "namespace": "pages",
          "patches": [
            {
              "op": "replace",
              "path": [
                "meta",
                "siteName"
              ],
              "value": "Site name"
            }
          ]
        }
      ]
    }
  ]
}
```

### publish

```json
{
  "target": "production"
}
```

### create-domain

```json
{
  "domain": "www.example.com"
}
```

### screenshot

```json
{
  "path": "/",
  "output": "screenshots/home.png",
  "viewport": {
    "width": 1440,
    "height": 900
  },
  "waitUntil": "load",
  "waitForTimeout": 250
}
```

```json
{
  "path": "/pricing",
  "output": "screenshots/pricing.png",
  "viewport": {
    "width": 1440,
    "height": 900
  },
  "waitUntil": "load",
  "waitForTimeout": 250
}
```

```json
{
  "url": "https://example.com",
  "output": "current.png",
  "viewport": {
    "width": 1440,
    "height": 900
  },
  "browser": "auto"
}
```

### screenshot.responsive

```json
{
  "path": "/pricing",
  "viewports": [
    {
      "width": 1440,
      "height": 900
    },
    {
      "width": 390,
      "height": 844
    }
  ],
  "source": "session"
}
```

### verify-page-responsive

```json
{
  "path": "/pricing",
  "viewports": [
    {
      "width": 1440,
      "height": 900
    },
    {
      "width": 390,
      "height": 844
    }
  ],
  "source": "session"
}
```

### screenshot.diff

```json
{
  "baselinePath": "baseline.png",
  "currentPath": "current.png",
  "outputDir": "visual-diff",
  "threshold": 0.1,
  "ignoreTopNormalizedY": 0,
  "expectedText": [
    "Pricing",
    "Start free"
  ],
  "expectedVisual": {
    "maxMismatchPercentage": 2,
    "maxChangedRegions": 3,
    "dominantColorChange": {
      "channel": "luminance",
      "direction": "increase",
      "minMagnitude": 10
    }
  }
}
```

### vision.install-ocr

```json
{
  "confirm": true
}
```

## Content Engine reference

Assets resources query Markdown, MDX, and JSON files stored in the Assets panel. The Builder and Webstudio MCP use the same structured query contract.

### MCP workflow

Use these tools in order when creating or changing an Assets resource:

1. Call `get-asset-field-catalog` to inspect standard fields and the fields currently observed in Markdown or MDX frontmatter and JSON files.
2. Call `validate-asset-query` to check the query structure, field paths, operators, and bounded operation counts.
3. Call `preview-asset-query` with concrete values and inspect its results and diagnostics.
4. Save the query with `create-assets-resource` or `update-assets-resource`.
5. Inspect saved queries with `list-assets-resources` or `get-assets-resource`. Use `delete-resource` to remove an obsolete resource.

Omit `query` when creating a resource to use the default many-result query for asset URLs and image dimensions. Set `values.query` to `null` when updating a resource to restore that default.

### Fields

Every asset has the standard fields below. Markdown or MDX frontmatter and JSON root fields appear under `properties`, for example `properties.slug` or `properties.author.name`. The field catalog reports their observed types, occurrence counts, optionality, and mixed-type state. A JSON content file must contain an object at its root.

| Field         | Observed type |
| ------------- | ------------- |
| `id`          | `string`      |
| `url`         | `string`      |
| `width`       | `number`      |
| `height`      | `number`      |
| `name`        | `string`      |
| `description` | `string`      |
| `path`        | `string`      |
| `key`         | `string`      |
| `folderId`    | `string`      |
| `extension`   | `string`      |
| `mimeType`    | `string`      |
| `size`        | `number`      |
| `createdAt`   | `string`      |
| `revision`    | `string`      |
| `excerpt`     | `string`      |

### Filters

Put conditions under `where.all` when every condition must match, or under `where.any` when at least one condition must match. Groups can be nested. A field path is an array such as `["properties", "slug"]`.

| Operator     | Builder label         | Compatible observed types                                |
| ------------ | --------------------- | -------------------------------------------------------- |
| `eq`         | equals                | `null`, `boolean`, `number`, `string`, `object`, `array` |
| `ne`         | does not equal        | `null`, `boolean`, `number`, `string`, `object`, `array` |
| `contains`   | contains              | `string`, `array`                                        |
| `startsWith` | starts with           | `string`                                                 |
| `endsWith`   | ends with             | `string`                                                 |
| `gt`         | greater than          | `number`, `string`                                       |
| `gte`        | greater than or equal | `number`, `string`                                       |
| `lt`         | less than             | `number`, `string`                                       |
| `lte`        | less than or equal    | `number`, `string`                                       |
| `in`         | is one of             | `null`, `boolean`, `number`, `string`, `object`, `array` |
| `exists`     | exists                | `null`, `boolean`, `number`, `string`, `object`, `array` |
| `isEmpty`    | is empty              | `string`, `object`, `array`                              |

The field catalog determines which operators fit a schemaless `properties` field. `exists` and `isEmpty` take a boolean. `in` takes an array. Other operators take one JSON value. When filtering `path`, enter the visible folder and file names, such as `Tutorial posts/article.mdx`; Webstudio handles canonical URL encoding. Existing encoded path filters remain supported.

### Saved values and preview values

Queries saved with `create-assets-resource` or `update-assets-resource` accept expressions for filter values, limits, and offsets. Wrap fixed values as literals. Pass a JavaScript expression string only when the value must be resolved at runtime:

```json
{
  "where": {
    "all": [
      { "field": ["extension"], "operator": "eq", "value": { "type": "literal", "value": "md" } },
      { "field": ["properties", "slug"], "operator": "eq", "value": "system.params.slug" }
    ]
  },
  "limit": { "type": "literal", "value": 1 },
  "offset": { "type": "literal", "value": 0 }
}
```

`validate-asset-query` and `preview-asset-query` execute a concrete query. Pass resolved JSON values such as `"hello-world"` and `1`, not expression wrappers or expression code.

### Sorting and pagination

Each sort has a field path and an `asc` or `desc` direction. Add `id` as the final sort when equal values must keep a stable order. `limit` defaults to 20 and `offset` defaults to 0. Static filters, limits, and offsets should use literal values. Use expressions only for runtime values such as `system.params.slug`.

### Result modes

| Value   | Behavior                                                                          |
| ------- | --------------------------------------------------------------------------------- |
| `many`  | Returns every matching item up to the limit. Use it for listings.                 |
| `one`   | Returns one item or `null`. It fails when more than one document matches.         |
| `first` | Returns the first sorted item or `null`. The query must include an explicit sort. |
| `last`  | Returns the last sorted item or `null`. The query must include an explicit sort.  |

Every returned item includes `id`. In `preview-asset-query`, a many result has `data.items`, `data.totalCount`, and `data.hasMore`; a single result has `data.item` and `data.totalCount`. A saved Assets resource exposes a many result as an ID-keyed map at `<dataSource>.data`, with `totalCount` and `hasMore` at `<dataSource>.meta`. It exposes a single result as the item or `null` directly at `<dataSource>.data`, with `totalCount` at `<dataSource>.meta`.

### Output modes

| Value    | Behavior                                                                                                         |
| -------- | ---------------------------------------------------------------------------------------------------------------- |
| `all`    | Returns every indexed property and the excerpt. Use selected fields when the page needs only part of a document. |
| `base`   | Returns no `properties` or excerpt. Set `includeMetadata` to include the standard file metadata.                 |
| `fields` | Returns the paths in `fields`. Set `includeMetadata` separately when the page also needs standard file metadata. |

Choose `fields` and disable `includeMetadata` when the page needs only selected values. Fields used only for static filtering or sorting do not need to be returned. When enabled, `includeMetadata` adds `name`, `description`, `path`, `key`, `folderId`, `extension`, `mimeType`, `size`, `createdAt`, `revision`. Every result includes `id`.

### Asset reference metadata

A plain local asset path in Markdown or MDX frontmatter, or in JSON properties, keeps the backward-compatible URL string result. Wrap the path in an exact `$ref` object only when consumers need structured Asset Manager metadata:

```yaml
featureImage:
  $ref: ./assets/hero.png
```

The reference declares only the relationship. The query decides which metadata enters its result and published database. A complete `properties.featureImage` selection returns the Asset Manager asset metadata together with its resolved `src`. This includes fields such as `id`, `name`, `description`, `mimeType`, `width`, and `height`; new asset metadata becomes available to references without changing the document. Select only the nested fields the page uses:

```json
{
  "output": {
    "mode": "fields",
    "includeMetadata": false,
    "fields": [
      ["properties", "title"],
      ["properties", "featureImage", "src"],
      ["properties", "featureImage", "description"]
    ]
  }
}
```

The selected value becomes an object. Bind an Image source to `post.properties.featureImage.src` and its alternative text to `post.properties.featureImage.description ?? post.properties.title`. A missing description therefore falls back to the article title without duplicating text in frontmatter.

External URLs remain ordinary strings. Existing local path strings also keep their URL string shape, so adopting structured references does not change existing queries or bindings.

### Content modes

| Value               | Behavior                                                                                                                                                                                                                                          |
| ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `none`              | Returns no file content. Use this for listings and any query that only needs fields or metadata.                                                                                                                                                  |
| `full`              | Embeds the complete UTF-8 file content in the content database. `maxBytes` defaults to 1 MiB and cannot be set higher. The query fails if a selected file is larger.                                                                              |
| `range`             | Embeds a byte range selected by `offset` and `length` in the content database. `length` cannot exceed 256 KiB.                                                                                                                                    |
| `markdown-body-ref` | Stores a reference to a Markdown or MDX body. Webstudio filters and paginates first, then reads only the selected bodies from Assets. `maxBytes` defaults to 1 MiB and cannot be set higher. The query fails if a selected source file is larger. |

Returned content has `encoding` and `text`. A range also reports its `offset`, returned `length`, and total file size. Use `markdown-body-ref` for Markdown or MDX article pages. It keeps article bodies out of the published content database and resolves relative links when the selected body is loaded.

### Preview diagnostics

`preview-asset-query` returns renderable results in `data` and non-bindable statistics in `__diagnostics__`. The diagnostic `scope` is always `query-preview`. Read the two capacity scopes separately:

* `query` measures the temporary database for the query being previewed.
* `database` measures the merged database for all reachable Assets resources in the project.

Only `database.usedBytes` counts toward `database.maxBytes`. Do not add the query and database sizes together.

| Diagnostic              | Meaning                                                                                                                                                                            |
| ----------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `usedBytes`             | Bytes included after applying the database limit.                                                                                                                                  |
| `maxBytes`              | Maximum bytes allowed for the scope.                                                                                                                                               |
| `unboundedBytes`        | Bytes the scope would use without the database limit.                                                                                                                              |
| `includedDocumentCount` | Documents included in the compiled database.                                                                                                                                       |
| `omittedDocumentCount`  | Documents omitted from the compiled database.                                                                                                                                      |
| `omissionReason`        | Why documents were omitted: `size` or `unavailable`.                                                                                                                               |
| `truncated`             | Whether the compiled database omitted content.                                                                                                                                     |
| `queryIssues`           | Structured nonfatal query warnings with a stable code and exact query path.                                                                                                        |
| `queryWarnings`         | Deprecated plain-text query warnings retained for compatibility.                                                                                                                   |
| `issues`                | All content errors and warnings found in files matched by the current query. Each diagnostic identifies its phase, code, message, asset, path, and source location when available. |
| `issueCount`            | Total errors and warnings in `issues`.                                                                                                                                             |
| `issuesTruncated`       | Always `false`; retained for response compatibility.                                                                                                                               |
| `artifacts`             | Optional query and merged compiled artifacts used by detailed Builder diagnostics.                                                                                                 |
| `unresolved`            | Optional query result before document references are resolved. It helps inspect the authored `$ref` values behind resolved output.                                                 |

If the merged database approaches its limit, remove duplicate reachable resources first. Then remove unused output fields or narrow the candidate documents. Prefer `markdown-body-ref` over embedded `full` content for Markdown or MDX articles.

### Document references

A document reference is an exact object with one string field:

```json
{ "$ref": "<relative-path>[#<fragment>]" }
```

Markdown and MDX references can appear in YAML frontmatter. JSON references can appear anywhere in the document. Any format can reference Markdown, MDX, or JSON. References do not run inside a Markdown or MDX body.

| Reference                           | Inserted value                                       |
| ----------------------------------- | ---------------------------------------------------- |
| `../authors/ada.json`               | The complete JSON value.                             |
| `../authors/ada.json#/profile/name` | The value at JSON Pointer `/profile/name`.           |
| `../authors/ada.md`                 | The complete Markdown source, including frontmatter. |
| `../authors/ada.md#frontmatter`     | The Markdown frontmatter object.                     |
| `../authors/ada.md#body`            | The Markdown body without frontmatter.               |

Resolve paths relative to the file containing the reference. JSON Pointer uses `~1` for `/` and `~0` for `~` in property names. URI-encode filename characters that have URL syntax, such as `%23` for `#`. Missing files, invalid fragments, and reference cycles fail instead of returning partial data.

### Query limits

| Limit                          | Value   |
| ------------------------------ | ------- |
| Query request                  | 512 KiB |
| Filter conditions              | 32      |
| Filter nesting depth           | 8       |
| Sort fields                    | 8       |
| Selected output fields         | 256     |
| Field path depth               | 9       |
| Default result count           | 20      |
| Maximum result count           | 1000    |
| Candidate documents            | 1000    |
| Documents in a reference graph | 2000    |
| Serialized query result        | 16 MiB  |
| Published content database     | 500 KiB |

### Content limits

| Limit                           | Value   |
| ------------------------------- | ------- |
| Markdown/MDX frontmatter        | 64 KiB  |
| Frontmatter nesting depth       | 8       |
| Frontmatter fields              | 256     |
| Frontmatter string              | 16 KiB  |
| JSON file                       | 1 MiB   |
| JSON nesting depth              | 8       |
| JSON fields                     | 256     |
| JSON string                     | 16 KiB  |
| Indexed properties per document | 64 KiB  |
| Generated excerpt               | 2 KiB   |
| MDX nesting depth               | 100     |
| MDX nodes                       | 20000   |
| MDX properties                  | 4000    |
| Loaded file                     | 1 MiB   |
| Loaded content per query        | 2 MiB   |
| Loaded files per query          | 20      |
| Loaded range                    | 256 KiB |
| Concurrent content reads        | 8       |

## Screenshot Verification

After the user explicitly requests or approves visual verification, call preview\.start once inside a long-running MCP server, then use screenshot({ path, viewport }) for fast repeated checks across multiple pages. Iterative mode is the default: after MCP mutations, path screenshots regenerate changed files and reload the requested route while keeping the server and browser alive. Use mode: "production" only for release-like verification. From one-shot shell calls or another process, use screenshot({ baseUrl, path, viewport }) to capture an already-running preview/site without generating, building, starting, or restarting preview. Use path values such as "/", "/pricing", or "/about" to capture specific generated routes. For responsive work, read list-breakpoints and capture one familiar device viewport inside each Builder breakpoint range before using vision. Screenshot waits for load by default, then fonts and two layout frames; pass waitForSelector for app readiness, waitUntil:"networkidle" for network-heavy pages, and waitForTimeout for final settling. When a baseline exists, use screenshot.diff for changed regions, OCR textAnalysis, and diff artifacts on each baseline/current screenshot pair. Outside MCP, use `webstudio screenshot --path /pricing --output pricing.png` for one temporary generated preview capture, or keep `webstudio preview` running and pass its absolute URL to `webstudio screenshot` for repeated captures.

## Related

* [CLI](/university/cli) – Install and use the Webstudio command-line interface
* [Content Engine](/university/foundations/content-engine) – Build a file-based site with Markdown and Assets queries
* [Content Engine reference](/university/foundations/content-engine/content-engine-reference) – Check query fields, modes, diagnostics, references, and limits
* [Share links](/university/foundations/share-links) – Grant the access used to link a Project
* [Publishing and custom domains](/university/foundations/publishing-and-custom-domains) – Publish the completed Project


# Inception

Generate, compare, refine, and reuse AI design directions with Inception.

Inception is Webstudio's standalone AI design exploration app. Use it to generate design directions from prompts, compare variants, refine existing results, edit selected elements, and reuse the generated HTML and Tailwind output.

Inception is separate from the Webstudio Builder. It is not a replacement for the Builder's AI, and it is not included in existing Webstudio plans. Inception has its own credits because each generation can use different AI models and external tools depending on the task.

{% hint style="info" %}
Inception is best for exploring and iterating on visual ideas. When you are ready to build a production site with Webstudio's visual editor, use the generated result as a reference or copy the HTML/Tailwind output.
{% endhint %}

{% embed url="<https://www.youtube.com/watch?v=KI5JHpzBK1s>" %}

<figure><img src="https://330243581-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTep4EGj6hSC54ozHujyK%2Fuploads%2Fgit-blob-e7f3918c01b4f1c6c42fca4c049dde55369c9295%2Finception-overview.webp?alt=media" alt="Inception workspace showing generated frames on the canvas and the prompt panel"><figcaption><p>Inception workspace</p></figcaption></figure>

## Core concepts

### Projects

Projects contain your Inception work. Open the main menu to switch projects, create a new project, rename a project, or delete a project.

Each project has its own boards, frames, generated results, and history.

### Boards

Boards help you organize different directions inside a project. Use boards for separate concepts, clients, pages, campaigns, or exploration phases.

From the boards panel you can:

* Create a board
* Switch between boards
* Rename a board
* Delete a board

<figure><img src="https://330243581-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTep4EGj6hSC54ozHujyK%2Fuploads%2Fgit-blob-59db557a21c7e48e9fe2c6c09f82091aaca037b3%2Finception-boards-panel.png?alt=media" alt="Inception boards panel showing project boards and board actions"><figcaption><p>Boards panel</p></figcaption></figure>

### Frames

A frame is one generated design result on the canvas. Frames can be moved, resized, renamed, cloned, previewed, edited, shared, and deleted.

Frames are useful because you can keep multiple directions visible at the same time and compare them side by side.

### Prompt panel

The prompt panel is where you tell Inception what to create or change. It contains:

* The prompt field
* Style picker
* Variant count
* Model selector
* Submit button

Press `Enter` to submit a prompt. Press `Shift + Enter` to add a line break.

<figure><img src="https://330243581-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTep4EGj6hSC54ozHujyK%2Fuploads%2Fgit-blob-da110bc43eb51a0c95a2edbe2409d4734c1542be%2Finception-prompt-panel.png?alt=media" alt="Inception prompt panel with style picker, variant count, model selector, and submit button"><figcaption><p>Prompt panel</p></figcaption></figure>

## Generating designs

To generate a design:

1. Select an existing frame, or start on an empty board
2. Write what you want to create in the prompt field
3. Optionally choose style options
4. Choose how many variants to generate
5. Choose a model, or keep the default
6. Submit the prompt

If the selected board has no frame yet, Inception creates one. If you choose multiple variants, Inception creates additional frames so you can compare different directions.

### Prompt history

Inception keeps recent prompt history in your browser. When the prompt field is empty, press the up arrow to recall previous prompts and their style settings.

### Variants

Variants create multiple frames from the same prompt. Choose between 1 and 4 variants.

Use variants when you want to compare different layouts, visual styles, or interpretations without writing the same prompt repeatedly.

### Model selector

The model selector chooses which AI model should generate the design. Inception gives you access to dozens of models, so you can choose the one that best fits the task, budget, and quality bar.

<figure><img src="https://330243581-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTep4EGj6hSC54ozHujyK%2Fuploads%2Fgit-blob-aa880d9a131e897919b298bc63388c3415c2f97f%2Finception-model-selector.webp?alt=media" alt="Inception model selector dropdown showing multiple AI model options"><figcaption><p>Model selector</p></figcaption></figure>

The available models can change depending on what you are doing.

For example:

* Regular page generation uses creative design/code models
* Selected element edits use the selected-edit model
* Image edits can show image-specific models or an Auto option

## Style picker

The style picker gives Inception extra creative direction before generation.

You can combine:

* **Presets** - ready-made visual directions
* **Layouts** - structural direction for the composition
* **Colors** - palette direction
* **Emotions** - mood and tone
* **Custom styles** - your own style references

Style choices are applied to new generations and broad frame edits. They are disabled while a frame is streaming or when you are editing a selected element.

<figure><img src="https://330243581-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTep4EGj6hSC54ozHujyK%2Fuploads%2Fgit-blob-9b70a2ef03077153a25b3a8173dd3ced10823cc8%2Finception-style-picker.webp?alt=media" alt="Inception style picker showing style preset options"><figcaption><p>Style picker presets</p></figcaption></figure>

### Custom styles

Custom styles let you create a reusable style direction from text or a reference image.

To create one:

1. Open Style picker
2. Open **Custom**
3. Choose **Create**
4. Add a label
5. Add text instructions or upload a reference image
6. Save the custom style

When you upload a reference image, Inception analyzes it and turns it into a style brief. You can save the result and reuse it in later prompts.

<figure><img src="https://330243581-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTep4EGj6hSC54ozHujyK%2Fuploads%2Fgit-blob-710aeca8ef1d310608cd1c2c8246df8266374a94%2Finception-custom-style.webp?alt=media" alt="Inception custom style workflow with a reference style"><figcaption><p>Custom style</p></figcaption></figure>

## Working with frames

<figure><img src="https://330243581-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTep4EGj6hSC54ozHujyK%2Fuploads%2Fgit-blob-b196220959391695e3e4b488459658746e0bff28%2Finception-frame-toolbar.png?alt=media" alt="Inception frame toolbar with frame name, breakpoint, history, preview, clone, and menu controls"><figcaption><p>Frame toolbar</p></figcaption></figure>

### Select and move frames

Click a frame to select it. Drag a frame to move it around the board. Selected frames can also be moved from the toolbar area.

### Resize frames

Use the resize handles around a frame to change its viewport size. Resizing helps you inspect how a design behaves at different dimensions.

### Rename frames

Use the frame name in the toolbar to rename a frame. Clear names make it easier to compare directions and review history.

### Create a new frame

Create a blank frame when you want to start a new direction manually or paste existing HTML/Tailwind into it.

Shortcut: `Ctrl + Shift + F`

### Clone or remix a frame

Clone a frame when you want to branch from an existing result without losing the current version.

Shortcut: `Alt + R`

### Delete a frame

Use the frame menu to delete a frame. If no element is selected, `Backspace` can delete the selected frame.

<figure><img src="https://330243581-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTep4EGj6hSC54ozHujyK%2Fuploads%2Fgit-blob-22c9096e9c048b16601b0664ae0d3ca15ce04ce3%2Finception-frame-menu.png?alt=media" alt="Inception frame menu with actions such as delete, undo, redo, preview, copy, paste, improve design, report, and share"><figcaption><p>Frame menu</p></figcaption></figure>

## Editing existing results

Inception can edit a whole frame or a selected part of a frame.

### Edit the selected element

Click an element inside a frame to select it, then write a prompt describing the change. The prompt panel shows a selection summary so you know which part will be edited.

Selected edits are useful for changes like:

* Rewriting a heading
* Changing a section layout
* Adjusting a button or card
* Adding or removing detail inside one area
* Restyling a specific component

Selected edits generate one update at a time.

<figure><img src="https://330243581-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTep4EGj6hSC54ozHujyK%2Fuploads%2Fgit-blob-1224cd94ce0f2c0e26d0a76d4433bcf3ef3f8892%2Finception-selected-edit.webp?alt=media" alt="Inception frame with a selected element and the prompt panel prepared for a selected edit"><figcaption><p>Selected element edit</p></figcaption></figure>

### Edit text directly

Some text can be edited directly in the frame. Select the text and press `Enter` to edit it when direct editing is available.

### Delete a selected element

Select an element and press `Backspace` to remove it.

### Image edits

When an image is selected, Inception can offer image-specific editing options. Depending on the target, you can:

* Regenerate the image
* Edit the whole image
* Edit a selected region
* Replace the image

The model selector changes for image edits and can include image models plus an Auto option.

<figure><img src="https://330243581-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTep4EGj6hSC54ozHujyK%2Fuploads%2Fgit-blob-33145518edb9107e4e53d9e9d5cfbc5200bac05d%2Finception-image-edit.png?alt=media" alt="Inception frame with an image selected and image edit controls available in the prompt panel"><figcaption><p>Image edit</p></figcaption></figure>

### Improve design

Use **Improve design** from the frame menu when you want Inception to refine a generated result without writing a detailed prompt from scratch.

Improve design uses a stronger model for design critique and refinement.

<figure><img src="https://330243581-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTep4EGj6hSC54ozHujyK%2Fuploads%2Fgit-blob-2d794a81e04f8ff3913efb2ba1233c8b871576fc%2Finception-improve-design.png?alt=media" alt="Inception frame menu showing the Improve design action"><figcaption><p>Improve design</p></figcaption></figure>

## Previewing and reviewing

### Preview mode

Preview opens the selected frame without canvas editing controls so you can inspect the result more like a visitor would.

Shortcut: `Ctrl + Shift + P`

Press `Escape` to exit preview mode.

### Responsive preview

Use the preview controls to inspect a frame at different device sizes. You can also resize the frame directly on the canvas to test other viewport dimensions.

### Frame history

Frame history records previous generated versions. Open history from the frame toolbar to review earlier results and jump back to a previous version.

Use history when you want to compare how a design evolved or recover a previous direction.

<figure><img src="https://330243581-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTep4EGj6hSC54ozHujyK%2Fuploads%2Fgit-blob-eee526db0a7be22b11b7e8f607c55508d033b5e1%2Finception-history.png?alt=media" alt="Inception frame history menu showing previous generated versions"><figcaption><p>Frame history</p></figcaption></figure>

### Undo and redo

Undo and redo move through frame versions.

* Undo frame version: `Ctrl + Z`
* Redo frame version: `Ctrl + Shift + Z`

## Sharing and reuse

### Share a frame

Use **Share** from the frame menu to share a generated frame.

### Copy HTML/Tailwind

Use **Copy HTML/Tailwind** to copy the selected frame's generated code.

Shortcut: `Ctrl + C`

### Paste HTML/Tailwind

Use **Paste HTML/Tailwind** to paste compatible code into a frame.

Shortcut: `Ctrl + V`

This is useful when you want to bring an external design into Inception for editing or create a new frame from copied output.

### Explore designs

Use **Explore designs** from the main menu to browse public examples and inspiration.

### Go to Webstudio Builder

Use **Go to Webstudio Builder** from the main menu when you want to move from exploration into Webstudio's production builder.

## Credits and billing

Inception uses credits. Open the main menu to view your balance, buy credits, or inspect generation history.

### Buy credits

The **Buy credits** dialog shows available credit packages. Credits are one-time purchases and can be bought again when needed.

<figure><img src="https://330243581-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTep4EGj6hSC54ozHujyK%2Fuploads%2Fgit-blob-ec8ccbec7d7672d7b2cce45e8b864064b0724a9a%2Finception-buy-credits.webp?alt=media" alt="Inception Buy credits dialog showing available credit packages"><figcaption><p>Buy credits</p></figcaption></figure>

### Balance

The **Balance** dialog shows your current balance and recent generation ledger. Each ledger entry includes:

* Amount
* Start time
* Duration or pending status

Generation cost varies by prompt, model, output size, and tools used. A small design can cost only a few cents, while a detailed full-page generation can cost more.

If a generation cannot start because of insufficient funds, Inception opens the Buy credits dialog.

<figure><img src="https://330243581-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTep4EGj6hSC54ozHujyK%2Fuploads%2Fgit-blob-7eedbe1c20bde5bc38bcf228f0b32c557555c78d%2Finception-balance.png?alt=media" alt="Inception Balance dialog showing current balance and recent generation ledger entries"><figcaption><p>Balance and generation ledger</p></figcaption></figure>

## Main menu

The main menu is where you can:

* Go to Webstudio Builder
* Explore designs
* Open projects
* Open or close boards
* Open account settings
* Zoom the canvas
* Hide UI
* Undo and redo frame versions
* Copy or paste HTML/Tailwind
* Improve design
* Report a problem
* Share a frame
* Remix a frame
* Create a new frame
* View balance
* Buy credits
* Restart the guided tour
* Open keyboard shortcuts
* Log out

<figure><img src="https://330243581-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTep4EGj6hSC54ozHujyK%2Fuploads%2Fgit-blob-dd6f96664793e5da27d625f69845914262861fe0%2Finception-project-menu.png?alt=media" alt="Inception main menu with project, board, canvas, frame, billing, and help actions"><figcaption><p>Main menu</p></figcaption></figure>

## Hide UI

Hide UI gives the canvas more space by hiding the surrounding interface.

Shortcut: `Ctrl + \`

Use the same shortcut again to show the UI.

## Guided tour

The guided tour introduces the main controls for generating, previewing, and managing frames. Open it from the main menu whenever you want to review the workflow.

## Keyboard shortcuts

Open the keyboard shortcuts dialog with `Shift + ?`.

Common shortcuts:

| Action                           | Shortcut           |
| -------------------------------- | ------------------ |
| Open keyboard shortcuts          | `Shift + ?`        |
| Toggle preview mode              | `Ctrl + Shift + P` |
| Hide UI                          | `Ctrl + \`         |
| Exit preview mode                | `Escape`           |
| Zoom in                          | `Ctrl + =`         |
| Zoom out                         | `Ctrl + -`         |
| Zoom to 100%                     | `Ctrl + 0`         |
| Create a new frame               | `Ctrl + Shift + F` |
| Remix frame                      | `Alt + R`          |
| Undo frame version               | `Ctrl + Z`         |
| Redo frame version               | `Ctrl + Shift + Z` |
| Copy HTML/Tailwind               | `Ctrl + C`         |
| Paste HTML/Tailwind              | `Ctrl + V`         |
| Edit selected text               | `Enter`            |
| Delete selected element or frame | `Backspace`        |

{% hint style="info" %}
On Mac, shortcuts are displayed with the Mac-specific modifier keys in the app.
{% endhint %}

## Troubleshooting

### The prompt field is disabled

Select a frame to start writing a message. The prompt field can also be disabled while the selected frame is streaming.

### Actions are disabled

Some frame actions require generated HTML. Preview, history, copy, share, improve design, and selected edits are unavailable on empty frames.

Actions are also disabled while a frame is streaming.

### A generation failed because of insufficient funds

Buy more credits from the Buy credits dialog, then run the prompt again.

### The result has a problem

Use **Report a problem** from the frame menu or main menu. Reporting helps the team investigate generation failures and quality issues.

## Inception and Webstudio AI

Inception is not the discontinued Webstudio AI assistant and is not a replacement for the Builder's future AI features. See [Webstudio AI](/university/webstudio-ai) for the full clarification.

## Related

* [Webstudio AI](/university/webstudio-ai) - Clarification about the discontinued Builder AI
* [Anatomy of the Webstudio builder](/university/foundations/anatomy-of-the-webstudio-builder) - Learn the production Builder interface
* [Copy-Paste](/university/foundations/copy-paste) - Paste HTML/Tailwind output into Webstudio
* [Page settings](/university/foundations/page-settings) - Configure production pages in Webstudio
* [Marketplace](/university/marketplace) - Use reusable Webstudio marketplace resources


# Radix UI Components

> See [Radix in Webstudio](https://webstudio.is/radix) for an overview of how Radix components work in Webstudio.

An open source component library optimized for fast development, easy maintenance, and accessibility built directly into Webstudio.

[Radix Documentation](https://www.radix-ui.com/primitives/docs/overview/introduction)

> An open-source UI component library for building high-quality, accessible design systems and web apps. Radix Primitives is a low-level UI component library with a focus on accessibility, customization and developer experience. You can use these components either as the base layer of your design system, or adopt them incrementally.
>
> \-- *Radix UI*

{% embed url="<https://www.youtube.com/watch?v=E3mZ6daJMjg>" %}

## Why Radix?

Radix was chosen because it prioritizes accessibility while giving full CSS control. All Radix components are **WAI-ARIA compliant**, ensuring proper keyboard navigation and screen reader support.

## Available Components

Radix enables you to create complex interactive elements without writing code:

* **Navigation Menu** – Horizontal navigation with dropdown support (mega menus)
* **Sheet** – Slide-out panels, perfect for mobile navigation
* **Dialog** – Modal windows and popups
* **Tooltip** – Hover hints and contextual information
* **Popover** – Click-triggered floating content
* **Accordion** – Expandable/collapsible content sections
* **Collapsible** – Simple show/hide functionality
* **Tabs** – Tabbed content interfaces
* **Select** – Customizable dropdown selects
* **Switch** – Toggle switches
* **Checkbox** – Fully customizable checkboxes
* **Radio Group** – Styled radio button groups
* **Label** – Accessible form labels

Each component can be fully styled using Webstudio's Style Panel while maintaining all accessibility features.


# Accordion

A vertically stacked set of interactive headings that each reveal an associated section of content.

## Features

* Full keyboard navigation.
* Supports horizontal/vertical orientation.
* Supports Right to Left direction.
* Can expand one or multiple items.
* Can be controlled or uncontrolled.

## How to use the Radix UI Accordion

The Accordion Component is in the "Components Panel" under the "Radix" section. Click on it or drag it onto the canvas. The Accordion will populate a template that's easy to adjust for your needs.

<figure><img src="https://330243581-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTep4EGj6hSC54ozHujyK%2Fuploads%2Fgit-blob-a832cd79276f1d1951c09a26446bf9106886aa24%2Fradix-components.png?alt=media&amp;token=b6e5f52b-b486-45c7-bef3-64bee6d14605" alt="Components panel with the Radix component group expanded" width="308"><figcaption><p>Radix UI components within Webstudio</p></figcaption></figure>

You can edit, delete or add more Accordions with ease by copying/pasting any of the "item" components within the Accordion.

<figure><img src="https://330243581-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTep4EGj6hSC54ozHujyK%2Fuploads%2Fgit-blob-96a4c58ae421e6250d31b271cd4c5dcc7cef29eb%2Fradix-accordion.png?alt=media&amp;token=54155df2-8e80-443f-b725-4efc1926e8e7" alt="Navigator showing the nested parts of a Radix Accordion"><figcaption></figcaption></figure>

## Changing an Accordion's content

To change the content of an Accordion that isn't currently displayed in the canvas, click on the "item content" instance that you want to change and it will be displayed.

<figure><img src="https://330243581-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTep4EGj6hSC54ozHujyK%2Fuploads%2Fgit-blob-d30f76090249a0d3a891cdf06b637e542e4cbf16%2Fradix-accordion-content.png?alt=media&amp;token=fccf04c5-02ac-401a-9eb7-e63c2ad6aa77" alt="Accordion Content selected on the canvas and in the Navigator"><figcaption></figcaption></figure>

## Animating Accordion content

The Accordion template keeps its content mounted with **Force Mount** and uses a closed-state `height: 0` rule. If you add a custom animation, keep an explicit collapsed style for `[data-state="closed"]`; animation-only examples can leave every panel visible when content stays mounted.

Radix temporarily sets inline `transition-duration: 0s` and `animation-name: none` while it measures content height. This can suppress the first open animation. Test both the first open and later open/close cycles when changing the animation.

## Using Collections within Accordions

To create an accordion with a [Collection](/university/core-components/collection) that iterates over data and outputs an accordion item for each entry, ensure each item has a unique value set, like this:

<figure><img src="https://330243581-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTep4EGj6hSC54ozHujyK%2Fuploads%2Fgit-blob-d52a738867f36c6737a9d6f4595e0fbcea8bc9b6%2Faccordion-collection.png?alt=media&amp;token=870f16c3-76e1-4209-b44d-60919509555d" alt="accordion collection unique item"><figcaption></figcaption></figure>

## Related Videos

{% embed url="<https://www.youtube.com/watch?v=PhCRSML1-G8>" %}

## Related

* [Collapsible](/university/radix/collapsible) – Single expandable/collapsible panel
* [Tabs](/university/radix/tabs) – Organize content into switchable panels
* [Dialog](/university/radix/dialog) – Modal window for focused content
* [Collection](/university/core-components/collection) – Generate accordion items dynamically from data


# Checkbox

Add accessible checkbox controls with Radix UI in Webstudio.

> Based on [Radix Checkbox](https://www.radix-ui.com/primitives/docs/components/checkbox)

The Radix Checkbox component provides a fully customizable checkbox with complete styling control while maintaining accessibility features.

## When to Use Radix Checkbox

Use Radix Checkbox when you need:

* Custom checkbox styling beyond native browser limits
* Animated check indicators
* Complex checkbox designs (custom icons, sizes, colors)
* Indeterminate state support

For simple forms where native styling is acceptable, the [native Checkbox](/university/core-components/checkbox) component works well.

## Structure

| Component              | Description                     |
| ---------------------- | ------------------------------- |
| **Checkbox**           | The main checkbox container     |
| **Checkbox Indicator** | Contains the check mark or icon |

## How to Use

1. Drag a **Checkbox** component from Components > Radix onto your canvas
2. The component includes a pre-configured indicator
3. Add a Label and associate it with the checkbox
4. Style all states (unchecked, checked, disabled)

## Properties

### Checkbox

| Property   | Description                             |
| ---------- | --------------------------------------- |
| `id`       | Unique identifier for label association |
| `name`     | Form field name for submission          |
| `value`    | Value sent when checked                 |
| `checked`  | Control checked state                   |
| `required` | Whether checking is required            |
| `disabled` | Disable the checkbox                    |

### Checkbox Indicator

| Property     | Description                          |
| ------------ | ------------------------------------ |
| `forceMount` | Keep indicator in DOM when unchecked |

## Basic Setup

```
Label (display: flex, align-items: center, gap: 8px)
├── Checkbox
│   └── Checkbox Indicator
│       └── Check icon (SVG or text)
└── "Accept terms and conditions"
```

## Styling States

### Data Attributes

| State         | Selector                     | Description       |
| ------------- | ---------------------------- | ----------------- |
| Unchecked     | `[data-state=unchecked]`     | Default state     |
| Checked       | `[data-state=checked]`       | Selected state    |
| Indeterminate | `[data-state=indeterminate]` | Partial selection |
| Disabled      | `[data-disabled]`            | Cannot interact   |

### Example Styles

**Checkbox container:**

* Default: border, background, size
* Checked: different background/border
* Focus: visible ring for accessibility

**Indicator:**

* Hidden when unchecked (opacity: 0 or display: none)
* Visible when checked (opacity: 1)
* Animate with transitions

## Indeterminate State

The indeterminate state is useful for "select all" checkboxes:

```
Checkbox (checked: "indeterminate")
└── Checkbox Indicator
    └── Minus icon (instead of check)
```

This indicates partial selection of child items.

## Custom Check Icons

Replace the default indicator content:

1. Add an SVG or icon component inside Checkbox Indicator
2. Style the icon size and color
3. Animate on check/uncheck

## Animation Tips

Add smooth check animations:

```css
/* Indicator transition */
Checkbox Indicator {
  transition:
    opacity 200ms,
    transform 200ms;
}

/* Scale animation on check */
[data-state="checked"] Checkbox Indicator {
  transform: scale(1);
}

[data-state="unchecked"] Checkbox Indicator {
  transform: scale(0);
  opacity: 0;
}
```

## Accessibility

Radix Checkbox provides:

* Full keyboard support (Space to toggle)
* ARIA attributes automatically applied
* Focus management
* Screen reader announcements

Ensure you:

* Always provide a visible label
* Maintain sufficient color contrast
* Include focus indicators

## Form Integration

Radix Checkbox works with Webstudio forms:

```
Form
├── Label
│   ├── Checkbox (name: newsletter)
│   │   └── Checkbox Indicator → ✓
│   └── "Subscribe to newsletter"
└── Button (type: submit)
```

The checkbox value is submitted with the form when checked.

## Comparison

| Feature       | Native Checkbox   | Radix Checkbox |
| ------------- | ----------------- | -------------- |
| Styling       | Limited           | Full control   |
| Custom icons  | No                | Yes            |
| Animations    | No                | Yes            |
| Indeterminate | JavaScript only   | Built-in       |
| Accessibility | Browser default   | WAI-ARIA       |
| Size          | 13-16px typically | Any size       |

## Related Components

* [Native Checkbox](/university/core-components/checkbox) - Simple HTML checkbox
* [Switch](/university/radix/switch) - Toggle on/off control
* [Radio Group](/university/radix/radio-group) - Single selection


# Collapsible

An interactive component which expands/collapses a panel.

## Features

* Full keyboard navigation.
* Adheres to the [Disclosure WAI-ARIA design pattern](https://www.w3.org/WAI/ARIA/apg/patterns/disclosure/).

## How to use the Radix UI Collapsible

The Collapsible Component is in the "Components Panel" under the "Radix" section. Click on it or drag it onto the canvas. The Collapsible will populate a template that's easy to adjust for your needs.

<figure><img src="https://330243581-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTep4EGj6hSC54ozHujyK%2Fuploads%2Fgit-blob-08692d424d800f76d035276348785bbd4b75ea92%2Fradix-collapsible.png?alt=media" alt="Components panel with the Radix Collapsible component and its parts" width="308"><figcaption><p>Radix UI Collapsible component within Webstudio</p></figcaption></figure>

The Collapsible component consists of three main parts:

1. **Collapsible**: The root component that controls the collapsible behavior.
2. **Collapsible Trigger**: The button that toggles the collapsible.
3. **Collapsible Content**: The component that contains the collapsible content.

## Customizing the Collapsible

To customize the Collapsible component:

1. **Collapsible Trigger**: Modify the trigger element to change what users click to expand/collapse the content. This can be text, an icon, or any combination of elements.
2. **Collapsible Content**: Edit the content section to add whatever elements you want to show/hide when the collapsible is toggled.
3. **Styling**: Any of the three components can be styled to match your design requirements.

## Keep closed content in the HTML

Select **Collapsible Content** and enable **Force Mount** to keep its content in the HTML while the Collapsible is closed. New Collapsible components enable this setting by default. Disable it to remove closed content from the HTML.

When **Force Mount** is enabled, add a closed-state CSS rule such as `height: 0` for `[data-state="closed"]`. Animation-only examples can leave the content visible because they assume Radix unmounts closed content.

Radix temporarily sets inline `transition-duration: 0s` and `animation-name: none` while it measures content height. This can suppress the first open animation, so test both the first open and later open/close cycles.

## Using Collapsible for UI Patterns

The Collapsible component is useful for various UI patterns:

* FAQ sections
* "Show more" content sections
* Settings or preferences panels
* Mobile navigation menus
* Detail views that can be expanded/collapsed

## Related

* [Accordion](/university/radix/accordion) – Multiple collapsible sections in a group
* [Tabs](/university/radix/tabs) – Organize content into switchable panels
* [Dialog](/university/radix/dialog) – Modal window for focused content
* [Sheet](/university/radix/sheet) – Sliding panel from screen edge


# Dialog

Create modal dialogs and popups with Radix UI in Webstudio.

> Based on [Radix Dialog](https://www.radix-ui.com/primitives/docs/components/dialog)

The Dialog component displays content in a modal window that overlays the page. When open, it renders content on top of an overlay that covers the entire window, making the content underneath inert.

## When to Use

Use Dialog for:

* Confirmation prompts ("Are you sure you want to delete?")
* Forms that require focused attention
* Important information that requires acknowledgment
* Content that should interrupt the user's workflow

## Structure

The Dialog component consists of several parts:

| Component              | Description                                            |
| ---------------------- | ------------------------------------------------------ |
| **Dialog**             | The root component that manages open/close state       |
| **Dialog Trigger**     | The button that opens the dialog                       |
| **Dialog Overlay**     | The semi-transparent backdrop behind the dialog        |
| **Dialog Content**     | The container for the dialog's content                 |
| **Dialog Title**       | The heading of the dialog (required for accessibility) |
| **Dialog Description** | Optional description text                              |
| **Dialog Close**       | Button to close the dialog                             |

## How to Use

1. Drag a **Dialog** component from Components > Radix onto your canvas
2. The component comes with a pre-configured structure including trigger, overlay, content, title, and close button
3. Customize the trigger button text and styling
4. Edit the Dialog Title and add your content inside Dialog Content
5. Style the overlay and content using the Style Panel

## Properties

Some commonly used properties (see the Settings panel for all available options):

| Property | Description                                                                                              |
| -------- | -------------------------------------------------------------------------------------------------------- |
| **open** | Controls whether the dialog is open. Use this to show/hide the dialog on the canvas for styling purposes |

## Styling States

Some common states you can style using the States selector (you can also create custom states):

* **Open** - When the dialog is visible
* **Closed** - When the dialog is hidden

## Related

* [Sheet](/university/radix/sheet) – Sliding panel variant of dialog
* [Popover](/university/radix/popover) – Floating panel for contextual content
* [Tooltip](/university/radix/tooltip) – Non-interactive hints on hover
* [Collapsible](/university/radix/collapsible) – Expandable content without overlay


# Label

Add accessible form labels with Radix UI in Webstudio.

> Based on [Radix Label](https://www.radix-ui.com/primitives/docs/components/label)

The Radix Label component provides an accessible label for form controls with built-in association handling.

## Overview

While the native HTML `<label>` element works for most cases, the Radix Label provides:

* Automatic ID generation and association
* Better focus behavior
* Consistent cross-browser behavior
* Integration with other Radix components

## When to Use

Use Radix Label when:

* Working with other Radix form components (Checkbox, Switch, Radio Group)
* You need consistent styling across all form labels
* Building custom form controls

For native HTML form elements, the [native Label](/university/core-components/label) component works well.

## Properties

| Property | Description                       |
| -------- | --------------------------------- |
| `for`    | ID of the associated form control |
| `id`     | Unique identifier                 |

## Basic Usage

### With Radix Checkbox

```
Label
├── Radix Checkbox
│   └── Checkbox Indicator → ✓
└── "Enable notifications"
```

When the label wraps the control, clicking anywhere on the label toggles the checkbox.

### With Explicit Association

```
Label (for: email-input)
  └── "Email Address"
Input (id: email-input)
```

## Styling

Style the Label like any text element:

* Font size and weight
* Color and spacing
* Cursor (pointer for clickable areas)

## Best Practices

1. **Always label form controls**: Every input needs a label
2. **Keep labels visible**: Don't rely only on placeholders
3. **Be concise**: Clear, short label text
4. **Position consistently**: Above or beside controls

## Related Components

* [Native Label](/university/core-components/label) - HTML label element
* [Checkbox](/university/radix/checkbox) - Radix checkbox
* [Switch](/university/radix/switch) - Toggle switch
* [Radio Group](/university/radix/radio-group) - Radio buttons


# Navigation Menu

Build accessible navigation menus with Radix UI in Webstudio.

> Based on [Radix Navigation Menu](https://www.radix-ui.com/primitives/docs/components/navigation-menu)

The Navigation Menu component creates accessible, multi-level navigation with dropdown menus. It's ideal for website headers with organized sections of links.

## When to Use

Use Navigation Menu for:

* Main website navigation in headers
* Complex navigation with multiple categories
* Mega menus with organized link groups
* Any multi-level navigation structure

## Structure

The Navigation Menu component consists of:

| Component                    | Description                     |
| ---------------------------- | ------------------------------- |
| **Navigation Menu**          | The root container              |
| **Navigation Menu List**     | Container for menu items        |
| **Navigation Menu Item**     | Wrapper for each top-level item |
| **Navigation Menu Trigger**  | Button that opens a dropdown    |
| **Navigation Menu Content**  | The dropdown panel              |
| **Navigation Menu Link**     | A navigation link               |
| **Navigation Menu Viewport** | Where dropdown content renders  |

## How to Use

1. Drag a **Navigation Menu** component from Components > Radix onto your canvas
2. The component comes with sample navigation structure
3. Edit Navigation Menu Links for simple links
4. Use Navigation Menu Trigger + Content for dropdown sections
5. Add your links and content inside each dropdown
6. Style all parts to match your design

### Simple Link (No Dropdown)

For a link without a dropdown, use just a Navigation Menu Link inside the Navigation Menu Item.

### Dropdown Menu

For a dropdown:

1. Add a Navigation Menu Trigger (the clickable text)
2. Add Navigation Menu Content (the dropdown panel)
3. Add your links and content inside Content

## Properties

Some commonly used properties (see the Settings panel for all available options):

### Navigation Menu Link

| Property | Description                |
| -------- | -------------------------- |
| **href** | The URL the link points to |

### Navigation Menu Content

| Property      | Description                                       |
| ------------- | ------------------------------------------------- |
| (Positioning) | Content automatically positions below the trigger |

## Styling States

Some common states (you can also create custom states):

* **Open** (`[data-state=open]`) - Dropdown is visible
* **Closed** (`[data-state=closed]`) - Dropdown is hidden
* **Active** - Current page indicator

## Building a Mega Menu

For large navigation with multiple columns:

1. Inside Navigation Menu Content, create a grid layout
2. Add multiple columns with Link groups
3. Optionally add featured content or images
4. Use the Viewport to control the dropdown container

## Related Videos

{% embed url="<https://www.youtube.com/watch?v=1xSrvEkXWws>" %}




---

[Next Page](https://docs.webstudio.is/llms-full.txt/1)

