# 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/4d88Dm4NB4HYqSp8UOQh" %}
[Contributing for Designers](/contributing/contributing-for-designers)
{% endcontent-ref %}

{% content-ref url="/pages/mFWAUCH8P2nKR4WbxRMO" %}
[Contributing for Developers](/contributing/contributing-for-developers)
{% 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 will appreciate Webstudio's flexibility and extensibility. It simplifies web development with its visual interface, yet provides full access to the underlying code. Developers can create custom components with complex logic or specific integrations, harnessing the power of coding while benefiting from Webstudio's streamlined visual workflows. As an open-source tool, Webstudio offers the freedom to extend functionality as needed and contribute to the enhancement of its features.

## 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.

### 🚀 Signup to 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 & 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="/files/oI6dKB0ctpITeNOqQmON" alt=""><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
* [Contributing for Designers](/contributing/contributing-for-designers) – Ways designers can contribute to Webstudio
* [Contributing for Developers](/contributing/contributing-for-developers) – How to contribute code to Webstudio


# 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>🚀 How can I use Webstudio today, and how will it evolve?</summary>

Currently, Webstudio excels in building super fast, responsive, dynamic websites. Future enhancements include linked CSS editors, animations engine, and real-time collaboration. Its open-source nature and API integration capabilities allow for extensive customization and connectivity with various services.

[Webstudio Vision Document](https://webstudiois.notion.site/Vision-f52ed097ccaa410eb05076981d446c2f)

</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" %}
[Data 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="/files/hBZslz1rKWMyS8XGuWAq" 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.

***

## 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="/files/kRLdJBnglfl94Yniqg36" alt="Workspace selector open in the Dashboard sidebar"><figcaption><p>Workspace selector</p></figcaption></figure>

<figure><img src="/files/ZJw5D44GAb9YaF2LBCI0" 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="/files/sX8kVf18dMD4m1MgkIKS" 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="/files/whitWai2Zb98aLbAa53H" 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="/files/gHgwJYHtnZ0dgsOcXM8L" 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 %}

***

## 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.

<figure><img src="/files/dJ60BuqM1JL6BqHWnWbY" alt="Canvas"><figcaption></figcaption></figure>

***

## 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.

### Renaming 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.

<figure><img src="/files/jEIaLMw7dPTJDf4e5nNE" alt="" width="373"><figcaption></figcaption></figure>

### 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 %}

<figure><img src="/files/d87U06U3CcGzoc7BzH2c" alt="Global Root"><figcaption><p>Global Root</p></figcaption></figure>

***

## 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 %}

<figure><img src="/files/EGCYQYpyn6hVnt9nbvVA" alt="Webstudio Breakpoints"><figcaption></figcaption></figure>

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.

<figure><img src="/files/OMmotsQaP2erRoHH5qJA" alt="Add components" width="279"><figcaption></figcaption></figure>

***

## 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.

### 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.

### Filtering and Sorting

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 find and remove all unused assets at once. A dialog lists every unused asset so you can review before confirming deletion. 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.

### 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.

<figure><img src="/files/4YCJfnFMXJlH8CxRr93I" alt="Style Panel" width="256"><figcaption></figcaption></figure>

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.

   <img src="/files/cQKkFfuX2EfcYBwgdjel" alt="Local style source" data-size="original">
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 [Data 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.

<figure><img src="/files/djkZOLd6B0fKsjNrtb38" alt="Mode switcher showing Design and Content modes"><figcaption></figcaption></figure>

***

## 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.

<figure><img src="/files/D43EN5xgODwHhQfdH6T0" alt="Builder View menu showing the Hide UI option and keyboard shortcut"><figcaption><p>Hide UI in the View menu</p></figcaption></figure>

***

## 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).

{% hint style="info" %}
Publishing currently takes around 45 seconds. During publishing, your Project is built into a JavaScript app and deployed to 300+ servers around the world.
{% endhint %}

## 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.

<figure><img src="/files/jEIaLMw7dPTJDf4e5nNE" alt="Navigator panel showing a tree of instances"><figcaption><p>The Navigator showing the page structure</p></figcaption></figure>

## 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

The Builder URL tracks the selected page and instance. Copy the browser URL to share a link that opens the same page and selects the same 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.

## 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="/files/dcWycJ5Sx5e1vKX9qp8n" 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="/files/4ZlCWTmnGX40rwgaxtqJ" 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="/files/u7NIbzwyWyyEIIS1ySoI" 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="/files/ZrFBwpWCMBroYbGfjCWo" 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="/files/IBXmmPL4EDWdcc7SFkJW" alt="Named grid areas list"><figcaption><p>Named grid areas</p></figcaption></figure>

<figure><img src="/files/85AL2H45teYML2KFqXJ7" 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="/files/iYwEkjktSBzOlxDf6cxz" 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="/files/HjZ20QeL5VkSO7VMWsFu" 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="/files/Tpcy2yTqk3ml1uroFYVT" 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.

<figure><img src="/files/YasmknclwSQxmqVPE3VI" alt="Breakpoint edit dialog showing min-width and max-width fields"><figcaption><p>Editing a breakpoint</p></figcaption></figure>

## 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="/files/v1rlgjPMNuhKmwd7liaM" 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.

For example, you can create one variable per color in your design system. Those colors are going to be used throughout your site and in Tokens.

### Layer 2: Tokens

[**Tokens**](/university/foundations/design-tokens) **are the next layer. They package up&#x20;*****multiple*****&#x20;styles.** 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, be sure to check out [Craft](/university/craft) — the standard guideline for building with Webstudio. It contains a library of expertly crafted CSS variables.
{% 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="/files/TPA5cUNr5BxYDuTVdbbF" 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="/files/bq6UCg8vfcjOJ3ngqm0w" 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="/files/6vIV2X4Uh1uvhZmkuUJS" 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="/files/sNSyUEybOrKnGKaJJ9ta" 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="/files/c6oUL9098uInST9knSLo" 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

<figure><img src="/files/d87U06U3CcGzoc7BzH2c" alt="Global Root in Webstudio" width="375"><figcaption><p>Global Root</p></figcaption></figure>

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.

<figure><img src="/files/wxITogqawPyHIZkTE3j4" alt=""><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

Design tokens enable the creation of consistent designs by packaging up multiple styles so that on any instance you add that Token, those styles will show up and stay in sync.

{% 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 Design tokens? <a href="#what-are-design-tokens" id="what-are-design-tokens"></a>

Design tokens are everything that you wish classes would be - a way to reuse styles without limitations.

* **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.
* **Universal format:** We didn’t invent Design tokens. There is an independent spec (by the [Design Tokens Community Group](https://design-tokens.github.io/community-group/format/)) that defines a data format for Tokens, meaning you can potentially import and export tokens between multiple apps. Soon you’ll be able to sync tokens between Webstudio and Figma through the [Tokens Studio for Figma](https://tokens.studio/) plugin!

## 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:

<table data-header-hidden><thead><tr><th></th><th></th><th data-hidden></th></tr></thead><tbody><tr><td><img src="/files/cQKkFfuX2EfcYBwgdjel" alt="Style sources" data-size="original"></td><td>The top of the Style Panel contains Style Sources. Here is where you can add new Tokens, select existing ones, and switch to Local styling.</td><td>The top section of the Style Panel is our <a href="https://www.reddit.com/r/diablo4/comments/148kfyt/psa_consolescontrollerbeginners_users/">Style Sources Input</a>. This is where you’ll create, style, and arrange your tokens.<br><br>When you select a component's instance on the canvas, the tokens you see inside this input are <em>sources</em> of the <em>styles</em> on that instance.</td></tr><tr><td><img src="/files/2ChUck45vMQ7FYxWs7FJ" alt="Convert local to token" data-size="original"></td><td>Want to style something immediately without making a Token? Use the Local Style Source. Styles applied on Local only impact that instance, but you can easily convert styles from Local to a new Token.</td><td>Want to style something immediately without making a token? Go for it. All component instances in Webstudio have this Local style source by default. Styles applied on Local are unique to an instance and can’t be re-used, but you can easily convert styles from Local to a new token through the token menu.</td></tr><tr><td><img src="/files/4GDwD8LDXUN9BHc6KoXX" alt="Adding a new token" data-size="original"></td><td>To make a new Token, click inside the Style Sources input, type a name, and hit enter.</td><td>To make a new token, click inside the Style Sources Input, type a name, and hit ENTER/RETURN.</td></tr><tr><td><img src="/files/Ihy5jskRVg36tO7IDcci" alt="Switching tokens" data-size="original"></td><td>The Token you’re currently styling will be blue in the Style Sources input, while others are gray. Simply click on another style source to select it. Any styling you do will be applied to the current Token and reflected across all instances of that Token.</td><td><p>The token you’re currently styling will be blue in the Style Sources Input, while others are gray. Simply click on another style source to select it.</p><p>Any styling you do will be applied to the current token and reflected across all instances of that token.</p><p>When you add a style, the label for that property will turn blue to show that it is applied on the current token.</p></td></tr><tr><td><img src="/files/vqtGeqMes8PVxtwNB1LX" alt="" data-size="original"></td><td><p>Hover the label for a helpful description of where the styles on this property come from.</p><p>In this case, we see that the width value that we just applied is coming from the Base breakpoint, the “new token” token on the Body instance. See <a href="/pages/gWk5LWLMojqZypn1DiPC#label-colors">Label Colors</a> to understand what the different colors mean.</p></td><td><p>Hover the label for a helpful description of where the styles on this property come from.</p><p>In this case we see that the width value that we just applied is coming from the Base breakpoint, the “new token” token, on the Body instance.</p></td></tr><tr><td><img src="/files/pXDF61afZbjPKVj42ZgN" alt="" data-size="original"></td><td>A circle in the Token indicates that there are no styles applied to the Token. This will go away as soon as you apply a style. For Local, a dot is added to the center of the circle when styles are added.</td><td></td></tr></tbody></table>

## 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 (e.g., "card" with padding, background, border-radius)
2. **Modifier tokens**: Contains variations (e.g., "small" for smaller padding, "featured" for highlight border)

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, border-radius
* "card-small" token: smaller padding
* "card-featured" token: accent border color

Apply "card" + "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.

<figure><img src="/files/4YCJfnFMXJlH8CxRr93I" alt="Style Panel on the right side of the builder"><figcaption><p>The Style Panel</p></figcaption></figure>

## 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.

**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="/files/sEDd6GXR69G0nonSK72h" 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="/files/t0A3VQXw7DUQCHLeZfua" 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 selectors are useful for advanced cases, but stick to the predefined states when possible — they are validated and shown in autocomplete.
{% 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="/files/kRAlZxx50JjSuK6dbfSp" 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="/files/wTnLI3izjb20CSAGiXFq" 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 create parent-child interactions — 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


# Data variables

Data variables enable the definition and use of a value throughout the page.

{% 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 %}

Data variables are defined on any instance in the navigator such as Global Root, Body, or Heading. Variables can be found on the right panel in the settings tab.

<figure><img src="/files/6Ufh6WTtZT2jPqcdA6yF" alt="Variables in the builder"><figcaption></figcaption></figure>

## 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 the Global Root, it will be 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 %}

## Global data variables

Variables defined on the **Global Root** are accessible across all pages in your project. This is powerful for:

* **Reusable contact information** – Define your email, phone, or address once and use it everywhere
* **Site-wide settings** – Store company name, social links, or other global data
* **CMS configuration** – Set up API endpoints that all pages can access
* **Slot compatibility** – Global variables work within Slots, enabling dynamic content in reusable components

### Setting Up Global Variables

1. In the Navigator, select **Global Root** (above Body)
2. In the Settings panel, add a new variable
3. Choose the variable type (String, JSON, Resource, etc.)
4. The variable is now accessible on any page

### Use Cases

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

* **Contact email**: Define once, bind to all contact forms and footers
* **Simple CMS**: Use JSON variables on Global Root for site-wide data that displays in Slots
* **API keys**: Store Resource configurations globally for consistent data fetching

## Variables

### System

System variable is a unique variable in that it exists by default, while all other variables are added by the user.

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 content like titles, descriptions, and labels.

### Number

A Number variable stores numeric values, including integers and decimals. It's useful for calculations, counters, and any numeric data that components might need to display or use in logic.

### Boolean

A Boolean variable represents a true or false value. It is ideal for toggling states, such as visibility.

### JSON

A JSON variable allows for structured data in JSON format. It is ideal for creating simple data structures used with [Collections](/university/core-components/collection) to iterate over each item, such as creating a dynamic gallery.

### Resource

A Resource variable gets its value from a fetch request, allowing data from a remote system to be used within Webstudio. For example, Resource can be used to interact with a REST API. While it can also be used to interact with a GraphQL API, it’s recommended to use the [GraphQL Resource](#graphql) instead.

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 %}

{% hint style="info" %}
**Copy & Paste:** Resources can be copied and pasted between projects. Select an instance with a Resource variable defined, copy it, and paste into another project – the Resource configuration will be included.
{% endhint %}

* URL – Where the resource is located.
* Method – A [request method](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods). 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 allows you to 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.

{% 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 %}

#### 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.

#### 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.

Webstudio sees approximately 45% of sub-requests (i.e., fetches from Cloudflare Workers) served from the cache. This means that, on average, roughly half of the time someone visits a page that uses Resources, such as a blog post, the request will go through to the origin/CMS.

### GraphQL

A GraphQL Resource variable gets its value from a GraphQL API, allowing data from a remote system to be used within Webstudio. While similar to [Resource](#resource), it’s unique in that the available fields are specifically designed for interacting with GraphQL APIs.

There are several fields available to configure the fetch request.

* **URL** – Where the resource is located.
* **Query** – A GraphQL query.
* **Variables** – A JavaScript object containing variables that will be passed into the request. This is commonly used to pass in parameters in a URL within a Dynamic Page. For example `{ slug: system.params.slug }` See [System Variable](#system) for more info.

{% 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 %}

### System Resource

A System Resource variable gets its value from internal data.

Available system resources:

* **Sitemap** – Contains data about the static pages on the website, commonly used to build a custom sitemap that combines dynamic data with static data. Refer to the [XML Node component](/university/core-components/xml-node#including-the-static-sitemap) for more info.
* **Current Date** – Returns the current date/time, useful for displaying "today's date" or calculating relative times. Can be formatted using the [Time component](/university/core-components/time).
* **Assets** – Queries project assets, including structured fields and file content from Markdown and JSON files. See [Content Engine](/university/foundations/content-engine) for a complete article workflow.

## 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="/files/9BGxs3ReYLIAC4IL7sLo" alt=""><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

* [Data 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="/files/RjFYoCfK608oYOgn40mf" 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="/files/FaaiZqM1vy0oBKsGyirx" 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.

The path can include dynamic parameters like `:name`, which could be made optional using `:name?`, or have a wildcard such as `/*` or `/:name*` to store the whole remaining part at the end of 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="/files/tLfjzXXyBf85DHQec8vZ" alt="Address Bar with hello-world"><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.

**Currently, Webstudio supports rich text if it's delivered in HTML or Markdown format.** In the future, we will support rich text regardless of the format delivered by adding conversion libraries for each CMS's flavor of rich text. For example, we'll automatically convert AST to HTML. Refer to [this issue](https://github.com/webstudio-is/webstudio/issues/3398) for more information.

In the meantime, advanced users can set up a proxy on Cloudflare Workers to convert rich text to HTML. However, this is outside the scope of Webstudio's support.

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="/files/cvEZMaUIlbvNT9BWVAHg" alt="Status Code field in Page Settings with expression bound"><figcaption><p>Binding an expression to the Status Code field in Page Settings</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="/files/R0GoWsz20MFZE4dqYAxa" alt="Box component with Show condition set to !cmsData.data[0].id"><figcaption><p>Setting the Show condition on the 404 content Box</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="/files/mGGTydEfXpvXdRf6OmRp" alt="Box component with Show condition set to cmsData.data[0].id"><figcaption><p>Setting the Show condition on the regular content Box</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
* [Data 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="/files/4g1kdpdiBSiNO0e8Uhcn" 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="/files/qxqrlDdkv59FQ5ruIwCG" 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="/files/TPA5cUNr5BxYDuTVdbbF" 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="/files/UPcC1VaVOhpTGxpNQvsQ" 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
* [Data variables](/university/foundations/variables) – Binding data to the canvas


# Copy-Paste

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

{% 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="/files/usdntJSYj7Q7FxO3CR7O" 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="/files/MKnYLyfJdxAgg2J2JaEp" 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](/files/ultnvaExxZLrf8nm7jeK) ![h1 token in webstudio](/files/qxqrlDdkv59FQ5ruIwCG)

### 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).**

<figure><img src="/files/DNzV3PeRFIDPQYQCLX0X" alt="Commands and search dialog open with results listed"><figcaption><p>Commands &#x26; search</p></figcaption></figure>

{% 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
* [Data 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.

<figure><img src="/files/E21ta2YydjplKOzbEO0y" alt="Assets panel showing uploaded images with search and filter controls"><figcaption><p>The Assets panel</p></figcaption></figure>

## 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, 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` 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`, `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 files also provide formatting controls and a preview.

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 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.

## 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.

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.

Duplicating a folder copies its nested folders and assets. Deleting a folder deletes everything inside it, so review the confirmation before continuing.

### 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:

<figure><img src="/files/MkrZ9WrGsJ3zzVNZLkgs" alt="Asset detail panel showing name, description, dimensions, MIME type, uses, and ID"><figcaption><p>Asset detail panel</p></figcaption></figure>

* **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 all unused assets** — click the brush icon in the Assets panel header to find and batch-delete all unreferenced assets in one action.

## 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 and JSON files in Assets
* [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, Assets queries, and dynamic pages.

Webstudio's Content Engine turns Markdown 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.

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).

The screenshots use the Webstudio Updates project as a working example. Its filenames, fields, and category filter differ from the tutorial values below.

<figure><img src="/files/aQ98CFG6tmE4YlEdGp3Z" alt="Assets panel showing Markdown articles and their assets folder"><figcaption><p>Markdown articles stored alongside their assets</p></figcaption></figure>

## 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 or JSON.

Good fits include:

* Small blogs with text and images.
* 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 documents. 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 Markdown files, upload images, build both pages, configure the Assets resources, add the Collection, bind the article directly, 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:

> Use Webstudio CLI to build a blog with the Content Engine. Store the articles as Markdown files, create `/blog` and `/blog/:slug`, 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. Images can live next to the articles or 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.

## 2. Create an article

1. Open `blog/posts` in the Assets panel.
2. Open the add menu and choose **Create text file**.
3. Name the file `hello-world.md`.
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: ./assets/hello-world.png
author: Ada Lovelace
---

# Hello world

Write the article here.
```

<figure><img src="/files/oAuNB6LMmz795kXejRn9" 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 refers to it with `./assets/hello-world.png`, relative to the Markdown file. When an Assets resource returns `properties.featureImage`, Webstudio turns that path into the published image URL. Bind the returned value directly to an image, background image, social image, download link, or another URL property.

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.

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. 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 add an Assets resource to its page-level **Dynamic data**:

1. Create a **System Resource** and choose **Assets**.
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`.
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** `"md"`
   * `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="/files/yq3AQdJbMhhhcV2urElh" 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`.
6. 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`.

Add another Assets resource to the page-level **Dynamic data**:

1. Create a **System Resource** and choose **Assets**.
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`, and `properties.author`.
4. Under **Result**, choose **Exactly one**.
5. Under **Content**, choose **Markdown body reference**.
6. Add these filters:
   * `extension` **equals** `"md"`
   * `folder id` **equals** the quoted `posts` folder ID
   * `properties.slug` **equals** `system.params.slug`
   * `properties.draft` **does not equal** `true`

<figure><img src="/files/2ApTQS0TxuAkyZ24K6WR" 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.

**Markdown body reference** keeps article bodies out of the published content database. Webstudio first finds the matching article, then loads only that Markdown body from Assets.

## 6. Bind the article

Bind the article components directly to `post.data`:

* Heading: `post.data.properties.title`
* Author: `post.data.properties.author`
* Image source: `post.data.properties.featureImage`
* Markdown Embed code: `post.data.content.text`

Add a [Markdown Embed](/university/core-components/markdown-embed) for the body. Style its nested headings, paragraphs, links, lists, and images once; the styles apply to every article.

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`
* **Status code**: `post.data ? 200 : 404`

The status expression returns a real 404 when no article matches the URL.

## 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 article, duplicate the Markdown 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 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.

Both supported source formats can reference either target format:

| Source   | Where references can appear | JSON target | Markdown target |
| -------- | --------------------------- | ----------- | --------------- |
| Markdown | YAML frontmatter            | Yes         | Yes             |
| JSON     | Anywhere in the document    | Yes         | Yes             |

References do not work inside a Markdown 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 and Markdown 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 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
* [Data 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 and JSON Assets with the Content Engine.

Assets resources query Markdown 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 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 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.

## 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`.

## 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 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 article pages. It keeps article bodies out of the published content database and resolves relative Markdown 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.                                                                                     |
| `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 articles.

## Document references

A document reference is an exact object with one string field:

```json
{ "$ref": "<relative-path>[#<fragment>]" }
```

Markdown references can appear in YAML frontmatter. JSON references can appear anywhere in the document. Either format can reference Markdown or JSON. References do not run inside a Markdown 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    |
| Serialized query result    | 16 MiB  |
| Published content database | 500 KiB |

## Content limits

| Limit                           | Value   |
| ------------------------------- | ------- |
| Markdown 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   |
| 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
* [Data 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.

<figure><img src="/files/LQytgEdfiR4WdNthBvGb" alt="Page settings panel showing Page name, Path, Status code, Redirect, and Language fields"><figcaption><p>General page settings</p></figcaption></figure>

## 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

Webstudio paths can be static or dynamic. Dynamic segments use a `:` prefix, making the page a [Dynamic Page](/university/foundations/cms#dynamic-pages).

| Pattern        | Matches                       |
| -------------- | ----------------------------- |
| `/about`       | One static route              |
| `/blog/:slug`  | One dynamic segment           |
| `/blog/:slug?` | Optional dynamic segment      |
| `/docs/*`      | Everything under `/docs/`     |
| `/docs/:path*` | Named wildcard under `/docs/` |

Path rules:

* Paths must start with `/`, except the home page path, which is empty
* Paths cannot contain repeating `/`
* Paths cannot end with `/`, except the home route `/`
* Wildcards such as `*` and `:path*` must be the final segment
* Parameter names can contain letters, numbers, and underscores

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="/files/PszuaHCSH4bTSiVI1kk1" 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="/files/V3loPQsO9yOnm5PfeELM" 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-domain-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.

<figure><img src="/files/GVdyGHV4zrFhz8ezl7ny" alt="SEO section showing Title, Description, Exclude from search, and search result preview"><figcaption><p>SEO settings with search result preview</p></figcaption></figure>

### 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.

<figure><img src="/files/fZSsJQWc9ahe3MXOAXNk" alt="Social image section with social preview card"><figcaption><p>Social image with preview</p></figcaption></figure>

## 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.

<figure><img src="/files/ArfEW72R1o68ZCjXutn3" alt="Custom metadata section with a property and content row filled in"><figcaption><p>Adding a custom meta tag</p></figcaption></figure>

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
* [Data 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.

## 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.

<figure><img src="/files/6NnuCnoCGqExESXNc7U2" alt="Project settings located in top left" width="261"><figcaption></figcaption></figure>

## 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.

## Publishing

### Atomic CSS

<figure><img src="/files/n9vBalxSSACo2rc94MfE" alt="Atomic css setting"><figcaption></figcaption></figure>

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 same syntax as page paths. See [Path syntax](/university/foundations/page-settings#path-syntax) for supported static routes, dynamic segments, optional segments, and wildcards.

<figure><img src="/files/JqyC6fyEvwbmbzi8qrP7" alt="Project Settings Authentication section with route, login, password fields, and protected route list"><figcaption><p>Project route authentication</p></figcaption></figure>

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-domain-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).

## 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


# 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.

<figure><img src="/files/djkZOLd6B0fKsjNrtb38" alt=""><figcaption></figcaption></figure>

## Design

Designer 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)
* 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 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="/files/gruFSbER1S8DN5lgfWbn" 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="/files/yMdrhxAEvoUXbp0I2kZ0" 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="/files/CvgAlE2BFJAKJYwCqYyZ" alt="Adding a template"><figcaption></figcaption></figure>
* 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 your changes when you are ready for them to go live. To do so, open the publish dialog by clicking “Publish” in the top right, then click “Publish”. It takes around 45 seconds for the site to publish. Note, “Publish” may be disabled if the designer didn’t enable the publishing permission for you.

## 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="/files/C4oYLsTU3d2jrJgO8Hxi" 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 %}

## Pre-publish checks

Webstudio checks publishable pages before starting a cloud publish or static export. These checks detect 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="/files/OIw0ldwj5iAvsmDxU4in" alt="CNAME and TXT records from new 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 %}

{% hint style="info" %}
Publishing currently takes around 45 seconds. During publishing, your Project is built into a JavaScript app and deployed to 300+ servers around the world.
{% endhint %}

Once your site is live, you can visit it by clicking the open icon next to the green checkmark.

***

## Publish to Staging

<figure><img src="/files/keZgHlL99OIO5ePq4lC3" alt="Publish to staging with production unchecked"><figcaption></figcaption></figure>

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](#id-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="/files/qskEYR4v1Ih92TtYnqMM" alt="Worker not found"><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](/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.

<figure><img src="/files/GhOp14vcsCucSHh62CtQ" alt="" width="278"><figcaption><p>Components panel</p></figcaption></figure>

***

## 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.

<figure><img src="/files/iyv9SUzjyIClGDv4Pwo6" alt="" width="239"><figcaption><p>Settings panel</p></figcaption></figure>

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="/files/BvDemHdlT7w7cJ3XjDEj" alt=""><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="/files/WrD07UMnJlO6fuI3qc8Q" alt="" 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="/files/kswjx3H6W6dBcWAtPCgW" alt="" 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

<figure><img src="/files/r5Rr0LA71iRgRVRsg4c9" alt=""><figcaption></figcaption></figure>

You can use the "Target" property to modify a link instance's behavior and define how linked content is displayed.

1. **Self**: When you set "Target" to "Self," the linked content will open in the same window or tab. This is the default behavior for links, and it maintains the browsing context.
2. **Blank**: If you choose "Blank," the linked content will open in a new tab or window, providing a separate browsing context.
3. **Parent and Top**: For web pages involving nested frames, "Parent" will direct the linked content to open in the parent frame or window, maintaining the hierarchy of frames.\
   \
   On the other hand, selecting "Top" will open the link instance in the top-level window, replacing all frames if there are any. This is useful when you want to break out of any frames and provide a full-page experience.

### 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="/files/Ps5LlLTARwmrSJNUuSq4" alt=""><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

![create a slot in Webstudio](/files/4g1kdpdiBSiNO0e8Uhcn)

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="/files/OaFsifjmKZ1mQ3U9zHFm" alt=""><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="/files/IhHH44Hycvpc9UKRzY6C" alt=""><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


# 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 **Code** in the Settings panel, or edit it on the canvas.
5. Select the matching **Language**.
6. Select a **Theme**.

You can bind **Code**, **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

Forms are used for searches, filters, and custom functionality.

> See [MDN: \<form>](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/form)

{% hint style="warning" %}
If you need the form submissions to be emailed to you/someone else or sent to a webhook, use [Webhook Form](/university/core-components/webhook-form).
{% endhint %}

## When to use Form

When providing visitors with a search field or filters, the submission data does *not* need to be emailed to you (can you imagine!?). Instead, the submission data is used to modify the page's contents, like a search field on a blog.

## How Form works

When submitting a form, its values are added to the URL as query parameters, triggering [Resources](/university/foundations/cms#resources) to re-fetch APIs and use the query parameters (available in the [System Variable](/university/foundations/variables#system)).

This all happens *without* a page refresh, improving user experience.

### An example

There's a blog listing page showing 100's blog posts over multiple pages and a Form added to the top with one text input, and its name field value is `searchBlog`.

<figure><img src="/files/Px7UeSTv6nVWBEX1U3qM" alt="search input with searchBlog name"><figcaption></figcaption></figure>

The goal is simple: only blogs containing the search term will be shown when a visitor submits the form.

**Here's how it works:**

1. A [Resource](/university/foundations/cms#resources) is already responsible for fetching all of those blog posts. The URL path in the Resource might look something like this: `/api/blogs`.
2. The Resource will be modified to include the input value (just one in this case, but you can add as many as you need) like this:

```javascript
`/api/blogs${system.search.searchBlog ? `?search=${system.search.searchBlog}` : ""}`;
```

This [expression](/university/foundations/expression-editor#expressions) contains the JavaScript Ternary Operator and Template Literals. It says, "Get the blogs, and if the `searchBlog` value is present, add the search filter to the API call; otherwise, don't."

All the search input values are available in [`system.search`](/university/foundations/variables#system), so if you have an input with a name, `helloWorld` you can access its value with `system.search.helloWorld`.

In summary, when submitting a form, its values are added to the URL as query parameters, which can then be used in [Resources](/university/foundations/cms#resources). Resources are re-fetched when query parameters change so that the Resource can use the values when the form is submitted.

## Form inputs

Many types of inputs can be added to a form.

There are currently two categories of form Components.

### **Webstudio Form Components**

These generate standard HTML inputs. While simple to implement, they have limited styling options, especially for elements like checkboxes, due to the constraints of HTML and CSS.

They can be found in Add Components > Forms:

<figure><img src="/files/PczoptiabOdzqMDeUjSC" alt="webstudio form components" width="299"><figcaption></figcaption></figure>

#### Input types

* **Button** – To submit the form, reset it, or for interactions like opening something. [Buttons are *not* links](/university/core-components/button). There are three types in Settings:
  * **Button**: Makes it a general element with no specific default action. Mainly used for interactions like opening something.
  * **Submit**: Will submit the form.
  * **Reset**: Will remove any data the user has put into a form.
* **Text Input** – By default, it's a simple text field, but it can be changed by going to Settings > Type and selecting one of the following types: number, search, time, hidden, color, date, datetime-local, email, month, password, range, tel, url, or week.

  ![text input that can be changed](/files/eyRpsEulJbGRnGIU8u42)
* **Select** – Provides a dropdown visitors can select one or more options.
* **Text Area** – Allows visitors to add multi-line data as part of their answers. It is similar to the “Text Input” component and the two share the same list of properties.
* **Checkbox** – Provides multiple options that the visitor can check or leave unchecked as part of their input.
* **Radio** – Gives the visitor a list of options and they have to select one.

### **Radix Form Components**

[Radix Form Components](/university/radix) provide enhanced styling and control by using dynamic elements. They work by hiding the actual HTML inputs (which have limited styling capabilities) and displaying customizable versions. When users interact with these styled elements, the system automatically updates the state of the hidden inputs, providing a visually rich and flexible user experience.

They can be found in Add Components > Radix:

<figure><img src="/files/EVASoz0gTLeDFdgx7tVE" alt="radix form components" width="299"><figcaption></figcaption></figure>

## Related

* [Webhook Form](/university/core-components/webhook-form) – Send form data to external services
* [Input](/university/core-components/input) – Text input fields
* [Button](/university/core-components/button) – Form submission buttons
* [Select](/university/core-components/select) – Dropdown selection
* [Checkbox](/university/core-components/checkbox) – Checkbox inputs
* [Radio Button](/university/core-components/radio-button) – Radio button inputs


# Webhook Form

Webhook Forms enable form submissions to get sent to an email address and optionally third-party services like Airtable, n8n, or Zapier.

{% hint style="info" %}
**Name change:** Webhook Forms used to be called "Forms." However, [Forms](/university/core-components/form) are now a different component intended for building searches and filters.
{% 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 > General**.

<img src="/files/chqvklsmUxZBwEhMCIQo" alt="Field to customize the recipient of the form submissions" data-size="original">
{% 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

You can add a Webhook Form Component to your canvas from **Components Panel > Data section**.

{% 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 Components](/university/core-components/form#form-inputs) 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="/files/smVJDFsRgPipLBlncJCZ" 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#form-inputs).

### 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) – Standard HTML forms
* [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 [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

Inputs should be placed within a [Form](/university/core-components/form) or [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) - Form with webhook submission
* [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" can be found in Components > Media, and you can place it on your canvas by dragging and dropping it or clicking it in the Components panel. ![Webstudio image component](/files/I3hsAGh0rhrN7YBkcTP1)

#### 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/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

![Vimeo to your canvas](/files/POcHfjhSnXHHwKc3BN2C)

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 background settings](/files/c75K3X4YhBmkyBrdJNJW)

## 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 background properties](/files/aQCjF5N1Jh8D1VSI7yrr)

* **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](/files/Qw0g1VUy0q6GQz4IY2tw)
2. Add a Vimeo component inside the Main Container.\
   ![Vimeo background image step 2](/files/mxVzBTRosciEUD8ztByC)
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](/files/UDpZKqpIN3NPeS2i40G7)
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](/files/r2FXovEdJ6qkBDmbeCRE)
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](/files/LWjMR2ZelRKf0qT3ZgML)

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](/files/BHp7kK13MPIUHd3vXDvr)
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](/files/4vJt95k7luftn11RdL0O)

## 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.

<figure><img src="/files/ssdmr3G5YrJ4YtnWyd1w" alt="YouTube Component in Webstudio Add Panel"><figcaption></figcaption></figure>

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="/files/oKcBkiJWXmBkf4ZkIOBj" 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="/files/UPcC1VaVOhpTGxpNQvsQ" 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="/files/OC1dRc4yN3L7VpwD34G0" 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**](#whats-an-array) **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="/files/MD0gOfeF5myzR20Wxa8n" alt=""><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="/files/TekYbvNqXWAmNnALQ3wJ" alt=""><figcaption><p>Tab Trigger with custom value</p></figcaption></figure> <figure><img src="/files/rKdepBt1x7vhZI45nuiK" alt=""><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="/files/9TbilVuKHi5tp88QC8nC" 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="/files/kLGz9AnkcWHnekywDtPV" 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="/files/Fb10oAgjSn0J4U5rS6NX" 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="/files/vaafoGs4DRvAUITjTP2Z" 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="/files/vTyhzDsEGwDtAfV7yAQO" 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="/files/sXgLiDt2aImYZr8gMe4j" 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.

## 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
* [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](#design-mode) ⬇️
2. [Content mode](#content-mode) ⬇️

## Content Block in Design mode

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.

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 Editable 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 %}

Every top-level instance within Templates will appear in Content mode like this:

<div><figure><img src="/files/uBTSVklWBztl8PibnNaf" alt="Templates in Design mode"><figcaption><p>Templates in Design mode</p></figcaption></figure> <figure><img src="/files/cizXuphhRJeAg5cgYx5V" alt="Template in Content mode"><figcaption><p>Template in Content mode</p></figcaption></figure></div>

Each time they insert a template, its copy appears as a direct child of the Content Block, alongside any initial content. The Templates container remains protected source material.

### Step 3: Add an initial setup (optional)

Optionally, you can add instances as direct children of Content Block.

<figure><img src="/files/ee03rTXLqBZvQBdWWfzV" alt="" 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 direct children of the Content Block. They cannot delete the Templates container, templates, or nested instances independently.

## Content Block in Content mode

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 Editable 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 Editable 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.

<figure><img src="/files/dRLcO7v6pwrSbSBhWgMu" alt="Content Blocks in the navigator"><figcaption></figcaption></figure>

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="/files/cizXuphhRJeAg5cgYx5V" alt=""><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="/files/NA5TN6ulB2XQktvq4EPM" alt="plus button changing to delete when holding option/alt"><figcaption><p>Hold option/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

* [Slot](/university/core-components/slot) – Reusable component slots
* [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="/files/ynpBvT4tN05BCZe8YgOP" alt=""><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”.

#### Code

<figure><img src="/files/yIm0lHR9Ij3VDlMlK6M7" alt=""><figcaption></figcaption></figure>

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.

### Run Script on Canvas

The "Run Script on Canvas" toggle allows you to not only render the HTML embed directly on the canvas, but also execute scripts. It will look exactly the same way as when you publish the site.

### 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).

***

### 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="/files/IoKElewhQo01RpEeYN4W" 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>" %}

<figure><img src="/files/t6ItiKd35Mhr0wYuYprO" alt="Head Slot" width="375"><figcaption></figcaption></figure>

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="/files/9rpDii1zpLwqa3avUmXt" alt=""><figcaption><p>Will display when Document Type is set to "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="/files/07QP8LPknhCu7oaSvO1B" alt="Page Settings Document Type 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

### Including 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. Fetch the static data by clicking Create Variable > System Resource (Type) > Sitemap (Resource)

   <figure><img src="/files/EeleswaaHK3CTTXoAykh" alt=""><figcaption></figcaption></figure>
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

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>"
```

Publish the Project in Webstudio Cloud before syncing when you need recent Builder changes in the local export.

## 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, publish the Project in Webstudio Cloud, then sync it locally:

```bash
npx webstudio sync
```

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#ssg-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":"<ws.element ws:tag=\"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.292.0**

{% 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

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": "<ws.element ws:tag='section' ws:style={css`padding: 32px; display: grid; gap: 16px;`}><ws.element ws:tag='h2'>Northstar Product OS</ws.element><ws.element ws:tag='p'>Reusable patterns for teams.</ws.element></ws.element>"
}
```

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
<ws.element
  ws:tag="section"
  style={{ padding: 32, borderRadius: 16 }}
>
  <ws.element ws:tag="h2">Operations Console</ws.element>
  <ws.element ws:tag="p">
    React-style object styles become editable Webstudio styles.
  </ws.element>
</ws.element>

<ws.element
  ws:tag="section"
  ws:tokens={[token("accent", css`color: #0f766e;`)]}
>
  <ws.element
    ws:tag="button"
    onClick={new ActionValue(["event"], expression`console.log(event)`)}
  >
    Track launch
  </ws.element>
</ws.element>

<ws.element ws:tag="section">
  <radix.Switch>
    <radix.SwitchThumb />
  </radix.Switch>
</ws.element>
```

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. `animation` is the component namespace for JSX such as `<animation.AnimateChildren>`; it 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 `<radix.Switch><radix.SwitchThumb /></radix.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.

## 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

`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.
* Unpublish staging or production deployments.
* 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`.
* 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`.
* For visual/design work, verify the rendered result with vision before finishing.

## Vision Verification Loop

Vision-capable AI can use MCP to see what it is building:

1. Make focused page/content/style changes with semantic MCP tools.
2. 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.
3. 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.
4. `preview.start` and `webstudio preview` install generated app dependencies under `.webstudio/preview` and reuse them across regenerations.
5. 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`.
6. Dependency installation honors `npm_config_cache`, including a caller-provided writable cache on Windows.
7. Do not add generated-preview dependencies to the repository root `package.json` or `pnpm-lock.yaml`.
8. If dependency installation fails, the error includes sanitized npm diagnostics. Check the reported npm and network configuration, then reinstall or update the Webstudio CLI if the problem persists.
9. 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.
10. 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.
11. 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.
12. 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.
13. An explicit occupied `port` fails immediately with `PREVIEW_PORT_IN_USE`. To capture a generated site already running in another process, pass its `baseUrl` with `path`; otherwise choose another port.
14. Automatic browser discovery checks system installations, configured browser paths, and Chromium installations in the Playwright browser cache.
15. 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.
16. 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.
17. 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.
18. 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.
19. 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`.
* Dependency installation honors `npm_config_cache`, including a caller-provided writable cache on Windows.
* Do not add generated-preview dependencies to the repository root `package.json` or `pnpm-lock.yaml`.
* If dependency installation fails, the error includes sanitized npm diagnostics. Check the reported npm and 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"
}
```

### 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"
}
```

### 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": "<ws.element ws:tag='article'><ws.element ws:tag='h2'>{expression`collectionItem.title ?? 'Untitled'`}</ws.element></ws.element>"
}
```

```json
{
  "parentInstanceId": "parent-id",
  "data": {
    "type": "json",
    "value": [
      {
        "name": "Starter"
      },
      {
        "name": "Pro"
      }
    ]
  },
  "itemFragment": "<ws.element ws:tag='div'>{expression`collectionItem.name`}</ws.element>"
}
```

### insert-fragment

```json
{
  "parentInstanceId": "parent-id",
  "fragment": "<ws.element ws:tag='section' ws:style={css`padding: 32px; display: grid; gap: 16px;`}><ws.element ws:tag='h2'>Northstar Product OS</ws.element><ws.element ws:tag='p'>Reusable patterns for teams.</ws.element></ws.element>"
}
```

```json
{
  "parentInstanceId": "parent-id",
  "fragment": "<ws.element ws:tag='section' style={{ padding: 32, borderRadius: 16 }}><ws.element ws:tag='h2'>Operations Console</ws.element><ws.element ws:tag='p'>Semantic section with React-style object styles converted into editable Webstudio styles.</ws.element></ws.element>"
}
```

```json
{
  "parentInstanceId": "parent-id",
  "fragment": "<ws.element ws:tag='section' ws:tokens={[token('accent', css`color: #0f766e;`)]} ws:style={css`display: grid; gap: 12px;`}><ws.element ws:tag='h2'>Token Example</ws.element><ws.element ws:tag='button' onClick={new ActionValue(['event'], expression`console.log(event)`)}>Track launch</ws.element></ws.element>"
}
```

```json
{
  "parentInstanceId": "parent-id",
  "fragment": "<ws.element ws:tag='section'><radix.Switch><radix.SwitchThumb /></radix.Switch></ws.element>"
}
```

### insert-fragment-verified

```json
{
  "parentInstanceId": "parent-id",
  "pagePath": "/pricing",
  "fragment": "<ws.element ws:tag='section'><ws.element ws:tag='h2'>Pricing</ws.element></ws.element>"
}
```

### 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 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 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 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.

### 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`.

### 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 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 article pages. It keeps article bodies out of the published content database and resolves relative Markdown 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.                                                                                     |
| `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 articles.

### Document references

A document reference is an exact object with one string field:

```json
{ "$ref": "<relative-path>[#<fragment>]" }
```

Markdown references can appear in YAML frontmatter. JSON references can appear anywhere in the document. Either format can reference Markdown or JSON. References do not run inside a Markdown 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    |
| Serialized query result    | 16 MiB  |
| Published content database | 500 KiB |

### Content limits

| Limit                           | Value   |
| ------------------------------- | ------- |
| Markdown 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   |
| 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

Inside a long-running MCP server, call preview\.start once, 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="/files/t8gWAJGTmkWDmkQTww8q" 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="/files/esNxC0Zx0l25djeqKVR0" 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="/files/NRSSi672me9JhAg6q9NZ" 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="/files/OvPz9AKlXBTWtyEwy4dc" 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="/files/4scZrW89EHrBx5IazU5v" 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="/files/C67gNJStWy8I0A6D6hXE" alt="Inception custom style workflow with a reference style"><figcaption><p>Custom style</p></figcaption></figure>

## Working with frames

<figure><img src="/files/So3YpcDWcM3RatDUer7a" 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="/files/JlM4QAzc7MeXUPX2LfLq" 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="/files/IddTQON2MqjXfN1sYno0" 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="/files/QYtJ2Oi85dYDkzlwlSkO" 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="/files/tjK64rl7E120Z5crDiX8" 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="/files/oCcisFLaGqB63DObTFBV" 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="/files/YNfCLQN594N7o6UQj3xV" 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="/files/8KcL2w67ywok0R5SWA5o" 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="/files/1JmEq1BDJgNVZEfOI7F4" 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="/files/FlzI6ENbJxru8yVW8ZQ1" alt="" 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="/files/snnvolSIvRkWWpxB6dwj" alt=""><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="/files/A1tb9eDvQTihpPVeFUsJ" alt=""><figcaption></figcaption></figure>

## 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="/files/MD0gOfeF5myzR20Wxa8n" 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="/files/VgOlW5OIwFEjxTb7USkC" alt="" 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.

## 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>" %}


# Popover

Create floating popover elements with Radix UI in Webstudio.

> Based on [Radix Popover](https://www.radix-ui.com/primitives/docs/components/popover)

The Popover component displays rich content in a floating panel that appears next to a trigger element. Unlike tooltips, popovers can contain interactive content like forms, buttons, and links.

## When to Use

Use Popover for:

* User profile dropdowns
* Quick edit forms
* Additional options or settings
* Preview cards
* Any interactive floating content

## Structure

The Popover component consists of several parts:

| Component           | Description                                      |
| ------------------- | ------------------------------------------------ |
| **Popover**         | The root component that manages open/close state |
| **Popover Trigger** | The element that opens the popover when clicked  |
| **Popover Content** | The floating panel containing your content       |
| **Popover Close**   | Optional button to close the popover             |

## How to Use

1. Drag a **Popover** component from Components > Radix onto your canvas
2. Customize the trigger (usually a button)
3. Add your content inside the Popover Content
4. Optionally add a Close button using Popover Close
5. Style the content panel and adjust positioning

## Properties

Some commonly used properties (see the Settings panel for all available options):

### Popover

| Property | Description                                                            |
| -------- | ---------------------------------------------------------------------- |
| **open** | Controls whether the popover is visible. Use for styling on the canvas |

### Popover Content

| Property        | Description                                         |
| --------------- | --------------------------------------------------- |
| **side**        | Preferred side: `top`, `right`, `bottom`, or `left` |
| **sideOffset**  | Distance in pixels from the trigger                 |
| **align**       | Alignment: `start`, `center`, or `end`              |
| **alignOffset** | Offset from the alignment position                  |

## Related

* [Tooltip](/university/radix/tooltip) – Non-interactive hints that appear on hover
* [Dialog](/university/radix/dialog) – Modal window for focused content
* [Sheet](/university/radix/sheet) – Sliding panel from screen edge
* [Select](/university/radix/select) – Dropdown selection component


# Radio Group

Add accessible radio button groups with Radix UI in Webstudio.

> Based on [Radix Radio Group](https://www.radix-ui.com/primitives/docs/components/radio-group)

The Radio Group component allows users to select a single option from a list of choices. Unlike checkboxes, only one radio button in a group can be selected at a time.

## When to Use

Use Radio Group for:

* Selecting one option from a small set (2-5 options)
* Payment method selection
* Shipping options
* Any mutually exclusive choice

## Structure

The Radio Group component consists of:

| Component                 | Description                              |
| ------------------------- | ---------------------------------------- |
| **Radio Group**           | The container managing selection state   |
| **Radio Group Item**      | An individual radio button               |
| **Radio Group Indicator** | The visual indicator (dot) when selected |

## How to Use

1. Drag a **Radio Group** component from Components > Radix onto your canvas
2. The component comes with sample radio items
3. Add or remove Radio Group Items as needed
4. Set a unique `value` on each Radio Group Item
5. Add labels next to each item for clarity
6. Style the items and indicators

## Using with Labels

For proper accessibility, wrap each radio in a [Label](/university/radix/label):

```
Label
├── Radio Group Item
│   └── Radio Group Indicator
└── Text "Option Name"
```

Or connect via `id` and `htmlFor` attributes.

## Using with Collections

To generate radio options dynamically:

1. Add a [Collection](/university/core-components/collection) inside the Radio Group
2. Add a Label containing a Radio Group Item
3. Bind the `value` property to a unique identifier
4. Bind the label text to your option's name

## Properties

Some commonly used properties (see the Settings panel for all available options):

### Radio Group

| Property     | Description                    |
| ------------ | ------------------------------ |
| **id**       | Unique identifier              |
| **name**     | Form field name for submission |
| **value**    | The currently selected value   |
| **required** | Whether selection is required  |

### Radio Group Item

| Property  | Description                                  |
| --------- | -------------------------------------------- |
| **value** | Unique identifier for this option (required) |

## Styling States

Some common states (you can also create custom states):

* **Checked** (`[data-state=checked]`) - Item is selected
* **Unchecked** (`[data-state=unchecked]`) - Item is not selected
* **Disabled** (`:disabled`) - Item cannot be selected
* **Focus** (`:focus-visible`) - Item has keyboard focus

## Related

* [Select](/university/radix/select) – Dropdown selection for choosing from a list of options
* [Switch](/university/radix/switch) – Toggle control for binary on/off choices
* [Label](/university/radix/label) – Accessible labels for form controls
* [Form](/university/core-components/form) – Container for form elements and submission handling
* [Collection](/university/core-components/collection) – Generate radio options dynamically from data


# Select

Create accessible dropdown selects with Radix UI in Webstudio.

> Based on [Radix Select](https://www.radix-ui.com/primitives/docs/components/select)

The Select component displays a dropdown list of options for users to choose from. It's a styled alternative to the native HTML `<select>` element with full keyboard navigation and accessibility support.

## When to Use

Use Select for:

* Choosing from a predefined list of options
* Form fields where users must pick one value
* Settings and preferences
* Any dropdown selection

## Structure

The Select component consists of several parts:

| Component                 | Description                          |
| ------------------------- | ------------------------------------ |
| **Select**                | The root component managing state    |
| **Select Trigger**        | The button showing the current value |
| **Select Value**          | Displays the selected option's text  |
| **Select Content**        | The dropdown panel                   |
| **Select Viewport**       | Scrollable container for items       |
| **Select Item**           | Individual selectable option         |
| **Select Item Text**      | The text label for an item           |
| **Select Item Indicator** | Checkmark shown on selected item     |

## How to Use

1. Drag a **Select** component from Components > Radix onto your canvas
2. The component comes pre-configured with sample items
3. Edit, add, or remove Select Items inside the Select Viewport
4. Set the `value` property on each Select Item to identify it
5. Style the trigger and dropdown to match your design

## Using with Collections

To populate Select options dynamically from data:

1. Add a [Collection](/university/core-components/collection) inside the Select Viewport
2. Add a Select Item inside the Collection
3. Bind the `value` property to a unique identifier from your data
4. Bind the Select Item Text to your label field

## Properties

Some commonly used properties (see the Settings panel for all available options):

### Select

| Property     | Description                                |
| ------------ | ------------------------------------------ |
| **name**     | Form field name for submission             |
| **value**    | The currently selected value               |
| **open**     | Controls dropdown visibility (for styling) |
| **required** | Whether a selection is required            |

### Select Item

| Property  | Description                                  |
| --------- | -------------------------------------------- |
| **value** | Unique identifier for this option (required) |

### Select Value

| Property        | Description                         |
| --------------- | ----------------------------------- |
| **placeholder** | Text shown when nothing is selected |

## Related

* [Radio Group](/university/radix/radio-group) – Selection from visible options
* [Switch](/university/radix/switch) – Toggle control for binary choices
* [Form](/university/core-components/form) – Container for form elements and submission handling
* [Collection](/university/core-components/collection) – Generate select options dynamically from data
* [Label](/university/radix/label) – Accessible labels for form controls




---

[Next Page](/llms-full.txt/1)

